"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const schematics_1 = require("@angular-devkit/schematics");
const testing_1 = require("@angular-devkit/schematics/testing");
const path = require("path");
const migrationsPath = path.join(__dirname, '../../migrations.json');
const runner = new testing_1.SchematicTestRunner('migrations', migrationsPath);
const MIGRATION = 'migration-v19-0-0-dynamic-form';
const DEFAULT_ANGULAR_JSON = JSON.stringify({
    projects: {
        app: {
            architect: {
                build: {
                    builder: '@angular-devkit/build-angular:application',
                    options: { styles: ['src/styles.scss'] },
                },
            },
        },
    },
});
const DEFAULT_PACKAGE_JSON = JSON.stringify({ dependencies: { quill: '^1.0.0' } }, null, 2) + '\n';
async function runMigration(files) {
    const tree = new testing_1.UnitTestTree(schematics_1.Tree.empty());
    if (!files['/angular.json'] && !files['angular.json']) {
        tree.create('/angular.json', DEFAULT_ANGULAR_JSON);
    }
    for (const [filePath, content] of Object.entries(files)) {
        tree.create(filePath, content);
    }
    return runner.runSchematic(MIGRATION, {}, tree);
}
// ─── Arquivos sem DynamicForm — não devem ser alterados ───────────────────────
describe('migrate-dynamic-form-v19 — arquivos sem dynamic-form', () => {
    it('não altera arquivo sem keywords do DynamicForm', async () => {
        const original = `import { Component } from '@angular/core';\n\n@Component({ selector: 'app-root', template: '' })\nexport class AppComponent {}\n`;
        const result = await runMigration({ 'src/app/app.component.ts': original });
        expect(result.readText('src/app/app.component.ts')).toBe(original);
    });
});
// ─── A1: Enum → String literal ────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — A1: enum → string literal', () => {
    it('converte FieldType.String → string', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new FormField({ type: FieldType.String, name: 'x' });
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'string'");
        expect(result.readText('src/app/comp.ts')).not.toContain('FieldType.String');
    });
    it('converte FieldType.Radio → radioButton', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new FormField({ type: FieldType.Radio, name: 'r' });
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'radioButton'");
    });
    it('converte FieldType.Text → textArea', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new FormField({ type: FieldType.Text, name: 't' });
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'textArea'");
    });
    it('converte FieldType.TextIA → contentGenerator', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new FormField({ type: FieldType.TextIA, name: 'ia' });
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'contentGenerator'");
    });
    it('converte FieldType.Date → date', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { CalendarField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new CalendarField({ type: FieldType.Date, name: 'dt' });
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'date'");
    });
    it('converte DynamicType.Row → row (enum v18)', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { DynamicType } from '@seniorsistemas/angular-components/dynamic-form';
const cfg = [{ type: DynamicType.Row, fields: [] }];
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'row'");
        expect(result.readText('src/app/comp.ts')).not.toContain('DynamicType.Row');
    });
    it('converte DynamicType.Section → section (enum v18)', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { DynamicType } from '@seniorsistemas/angular-components/dynamic-form';
const cfg = [{ type: DynamicType.Section, fields: [] }];
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'section'");
    });
    it('converte GridType.Row → row', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { GridType } from '@seniorsistemas/angular-components/dynamic-form';
const cfg = [{ type: GridType.Row, fields: [] }];
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'row'");
        expect(result.readText('src/app/comp.ts')).not.toContain('GridType.Row');
    });
    it('converte StructureType.Section → section', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { StructureType } from '@seniorsistemas/angular-components/dynamic-form';
const cfg = [{ type: StructureType.Section, fields: [] }];
`,
        });
        expect(result.readText('src/app/comp.ts')).toContain("'section'");
        expect(result.readText('src/app/comp.ts')).not.toContain('StructureType.Section');
    });
});
// ─── A2: Construtores → plain objects ─────────────────────────────────────────
describe('migrate-dynamic-form-v19 — A2: construtores → plain objects', () => {
    it('converte new FormField({...}) → plain object', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new FormField({ type: FieldType.String, name: 'x', mask: '999' });
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).not.toContain('new FormField');
        expect(text).toContain("{ type: 'string', name: 'x', mask: '999' }");
    });
    it('converte new TextField({...}) → plain object em array', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { TextField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const fields = [new TextField({ type: FieldType.String, name: 'n' })];
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).not.toContain('new TextField');
        expect(text).toContain("{ type: 'string', name: 'n' }");
        expect(text).not.toContain('as TextFieldConfig'); // inside array, no cast needed
    });
    it('converte new CalendarField({...}) → plain object', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { CalendarField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const fields = [new CalendarField({ type: FieldType.Date, name: 'dt', appendTo: 'body' })];
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).not.toContain('new CalendarField');
        expect(text).toContain("'date'");
        expect(text).toContain("appendTo: 'body'");
    });
    it('adiciona type cast quando objeto não está em array (isolado)', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { TextField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new TextField({ type: FieldType.String, name: 'x' });
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('as TextFieldConfig');
    });
    it('não adiciona type cast quando FormField está em array', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const fields = [new FormField({ type: FieldType.String, name: 'x' })];
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).not.toContain('as DynamicType');
        expect(text).not.toContain('as FormField');
    });
    it('envolve em parênteses quando new XxxField({...}) é o corpo conciso de uma arrow function', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { TextField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const creators = {
    string: () => new TextField({ type: FieldType.String, name: 'x' }),
};
`,
        });
        const text = result.readText('src/app/comp.ts');
        // a leading `{` right after `=>` would be parsed as a block, not an object literal —
        // the parenthesized form below is the only syntactically valid rendering
        expect(text).toContain("string: () => ({ type: 'string', name: 'x' } as TextFieldConfig),");
    });
    it('envolve em parênteses quando new XxxField({...}) é uma expression statement isolada', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { TextField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
new TextField({ type: FieldType.String, name: 'x' });
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain("({ type: 'string', name: 'x' } as TextFieldConfig);");
    });
    it('importa a interface XxxFieldConfig usada em cast isolado', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { TextField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new TextField({ type: FieldType.String, name: 'x' });
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('as TextFieldConfig');
        expect(text).toMatch(/import\s*\{[^}]*\bTextFieldConfig\b[^}]*\}\s*from\s*'@seniorsistemas\/angular-components\/dynamic-form'/);
    });
    it('converte new XxxField(variavel) — argumento não é objeto literal inline', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { CalendarField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
function createCalendar(config: any) {
    const calendar = new CalendarField(config);
    calendar.appendTo = 'body';
    return calendar;
}
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).not.toContain('new CalendarField');
        expect(text).not.toContain('CalendarField,');
        expect(text).toContain('const calendar = config as CalendarFieldConfig;');
        expect(text).toMatch(/import\s*\{[^}]*\bCalendarFieldConfig\b[^}]*\}\s*from\s*'@seniorsistemas\/angular-components\/dynamic-form'/);
    });
    it('converte new XxxField(variavel) dentro de array — sem cast', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { CalendarField } from '@seniorsistemas/angular-components/dynamic-form';
function build(config: any) {
    return [new CalendarField(config)];
}
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).not.toContain('new CalendarField');
        expect(text).toContain('return [config];');
    });
});
// ─── A2b: Boolean + representedBy:'switch' → booleanSwitch ───────────────────
describe('migrate-dynamic-form-v19 — A2b: Boolean + representedBy → booleanSwitch', () => {
    it('converte boolean+representedBy:switch → booleanSwitch e remove representedBy', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new FormField({ type: FieldType.Boolean, representedBy: 'switch', name: 'active' });
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain("'booleanSwitch'");
        expect(text).not.toContain('representedBy');
        expect(text).not.toContain("'boolean'");
    });
    it('não remove representedBy quando valor não é switch', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = new FormField({ type: FieldType.Boolean, representedBy: 'checkbox', name: 'active' });
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('representedBy');
        expect(text).toContain("'boolean'");
    });
});
// ─── A3: Type annotations ─────────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — A3: type annotations', () => {
    it('converte FormField[] → DynamicType[]', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField } from '@seniorsistemas/angular-components/dynamic-form';
export class C { fields: FormField[] = []; }
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('DynamicType[]');
        expect(text).not.toContain('FormField[]');
    });
    it('converte Field[] → DynamicType[]', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { Field } from '@seniorsistemas/angular-components/dynamic-form';
export class C { fields: Field[] = []; }
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('DynamicType[]');
        expect(text).not.toContain('Field[]');
    });
    it('converte DynamicConfig[] → DynamicStructure[]', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { DynamicConfig } from '@seniorsistemas/angular-components/dynamic-form';
export class C { get configs(): DynamicConfig[] { return []; } }
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('DynamicStructure[]');
        expect(text).not.toContain('DynamicConfig[]');
    });
    it('converte FormField em parâmetro de método → DynamicType', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField } from '@seniorsistemas/angular-components/dynamic-form';
export class C { build(fields: FormField[]): void {} }
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('DynamicType[]');
    });
});
// ─── A4: Import cleanup ───────────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — A4: import cleanup', () => {
    it('remove FormField, FieldType, TextField dos imports', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType, TextField, DynamicFormModule } from '@seniorsistemas/angular-components/dynamic-form';
const f = [new FormField({ type: FieldType.String, name: 'x' })];
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).not.toContain("import { FormField");
        expect(text).not.toContain('FormField');
        expect(text).not.toContain('TextField');
        expect(text).toContain('DynamicFormModule'); // DynamicFormModule must be kept
    });
    it('adiciona DynamicType ao import quando usado como type annotation', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField } from '@seniorsistemas/angular-components/dynamic-form';
export class C { fields: FormField[] = []; }
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('DynamicType');
        expect(text).toContain(`from '@seniorsistemas/angular-components/dynamic-form'`);
    });
    it('adiciona DynamicStructure quando DynamicConfig é convertido', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { DynamicConfig } from '@seniorsistemas/angular-components/dynamic-form';
export class C { get c(): DynamicConfig[] { return []; } }
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('DynamicStructure');
    });
    it('mantém FieldType no import se usado como type annotation', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FieldType } from '@seniorsistemas/angular-components/dynamic-form';
type MyMap = Record<string, FieldType>;
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain('FieldType');
    });
});
// ─── A5: Editor field ─────────────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — A5: Editor field', () => {
    it('converte FieldType.Editor → editor e mantém props como modules', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = [new FormField({ type: FieldType.Editor, name: 'desc', modules: { toolbar: [] } })];
`,
            'src/app/app.config.ts': `
import { ApplicationConfig } from '@angular/core';
export const appConfig: ApplicationConfig = { providers: [] };
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain("'editor'");
        expect(text).toContain('modules');
        expect(text).not.toContain('FieldType.Editor');
    });
    it('adiciona provideEditorField() ao app.config.ts quando Editor detectado', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = [new FormField({ type: FieldType.Editor, name: 'desc' })];
`,
            'src/app/app.config.ts': `
import { ApplicationConfig } from '@angular/core';
export const appConfig: ApplicationConfig = { providers: [] };
`,
        });
        const configText = result.readText('src/app/app.config.ts');
        expect(configText).toContain('provideEditorField()');
        expect(configText).toContain("from '@seniorsistemas/angular-components/optional-fields/editor'");
    });
    it('remove quill do package.json quando Editor não é usado', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = [new FormField({ type: FieldType.String, name: 'x' })];
`,
            '/package.json': DEFAULT_PACKAGE_JSON,
        });
        const pkg = JSON.parse(result.readText('/package.json'));
        expect(pkg.dependencies?.quill).toBeUndefined();
    });
    it('mantém quill no package.json quando Editor é usado', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = [new FormField({ type: FieldType.Editor, name: 'desc' })];
`,
            'src/app/app.config.ts': `
import { ApplicationConfig } from '@angular/core';
export const appConfig: ApplicationConfig = { providers: [] };
`,
            '/package.json': DEFAULT_PACKAGE_JSON,
        });
        const pkg = JSON.parse(result.readText('/package.json'));
        expect(pkg.dependencies?.quill).toBeDefined();
    });
});
// ─── A5b: angular.json styles ─────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — A5b: angular.json styles', () => {
    it('adiciona styles.css ao angular.json', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { FormField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';
const f = [new FormField({ type: FieldType.String, name: 'x' })];
`,
        });
        const json = JSON.parse(result.readText('/angular.json'));
        const styles = json.projects.app.architect.build.options.styles;
        expect(styles).toContain('node_modules/@seniorsistemas/angular-components/styles.css');
    });
});
// ─── B: HTML [fields] → [configs] ─────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — B: HTML [fields] → [configs]', () => {
    it('substitui [fields]="fields" por [configs]="fieldsConfigs" no HTML', async () => {
        const result = await runMigration({
            'src/app/comp.html': `<s-dynamic-form [form]="form" [fields]="fields"></s-dynamic-form>`,
            'src/app/comp.ts': `
import { DynamicFormModule } from '@seniorsistemas/angular-components/dynamic-form';
export class C { fields = []; }
`,
        });
        const html = result.readText('src/app/comp.html');
        expect(html).toContain('[configs]="fieldsConfigs"');
        expect(html).not.toContain('[fields]');
    });
    it('gera getter protected get fieldsConfigs() no .ts correspondente', async () => {
        const result = await runMigration({
            'src/app/comp.html': `<s-dynamic-form [form]="form" [fields]="fields"></s-dynamic-form>`,
            'src/app/comp.ts': `
import { DynamicFormModule } from '@seniorsistemas/angular-components/dynamic-form';
export class C { fields = []; }
`,
        });
        const ts = result.readText('src/app/comp.ts');
        expect(ts).toContain('get fieldsConfigs()');
        expect(ts).toContain('dynamicTypeToDynamicStructure');
        expect(ts).toContain('DynamicStructure');
    });
    it('trata [fields]="tab.fields" (membro de objeto) com helper method', async () => {
        const result = await runMigration({
            'src/app/comp.html': `<s-dynamic-form [form]="form" [fields]="tab.fields"></s-dynamic-form>`,
            'src/app/comp.ts': `
import { DynamicFormModule } from '@seniorsistemas/angular-components/dynamic-form';
export class C { tab: any = {}; }
`,
        });
        const html = result.readText('src/app/comp.html');
        const ts = result.readText('src/app/comp.ts');
        expect(html).toContain('[configs]="dynamicTypeToConfigs(tab.fields)"');
        expect(ts).toContain('dynamicTypeToConfigs(fields: DynamicType[]): DynamicStructure[]');
    });
    it('não altera HTML sem [fields] no s-dynamic-form', async () => {
        const original = `<s-dynamic-form [form]="form" [configs]="configs"></s-dynamic-form>`;
        const result = await runMigration({
            'src/app/comp.html': original,
            'src/app/comp.ts': `export class C {}`,
        });
        expect(result.readText('src/app/comp.html')).toBe(original);
    });
});
// ─── Cenário completo ─────────────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — cenário completo', () => {
    it('migra arquivo completo com múltiplos tipos de campo', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { Field, FieldType, FormField, CalendarField, SelectField, DynamicConfig, DynamicFormModule }
    from '@seniorsistemas/angular-components/dynamic-form';

export class MyComponent {
    fields: Field[] = [
        new FormField({ type: FieldType.String, name: 'nome', label: 'Nome' }),
        new CalendarField({ type: FieldType.Date, name: 'dt', label: 'Data' }),
        new SelectField({ type: FieldType.Enum, name: 'status', label: 'Status', options: [] }),
    ];

    get configs(): DynamicConfig[] {
        return [{ type: 'row', fields: this.fields }];
    }
}
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain("{ type: 'string', name: 'nome', label: 'Nome' }");
        expect(text).toContain("{ type: 'date', name: 'dt', label: 'Data' }");
        expect(text).toContain("{ type: 'enum', name: 'status', label: 'Status', options: [] }");
        expect(text).toContain('DynamicType[]');
        expect(text).toContain('DynamicStructure[]');
        expect(text).toContain('DynamicFormModule');
        expect(text).not.toContain('FormField');
        expect(text).not.toContain('CalendarField');
        expect(text).not.toContain('SelectField');
        expect(text).not.toContain('FieldType');
        expect(text).not.toContain('DynamicConfig');
    });
});
// ─── Regressão: mapa de factories por tipo (caso real reportado) ─────────────
describe('migrate-dynamic-form-v19 — regressão: mapa de creators por FilterType', () => {
    it('migra um Record<string, () => FormField> com new XxxField em corpo conciso e por variável, sem quebrar o parsing', async () => {
        const result = await runMigration({
            'src/app/comp.ts': `
import { TextField, CalendarField, FieldType } from '@seniorsistemas/angular-components/dynamic-form';

export class FormFilterComponent {
    private createField(filterType: string, field: any) {
        const creators = {
            string: () => new TextField({ ...field, type: FieldType.String }),
            date: () => this.createCalendar({ ...field, type: FieldType.Date }),
        };
        return creators[filterType]();
    }

    private createCalendar(config: any) {
        const calendar = new CalendarField(config);
        calendar.appendTo = 'body';
        return calendar;
    }
}
`,
        });
        const text = result.readText('src/app/comp.ts');
        expect(text).toContain("string: () => ({ ...field, type: 'string' } as TextFieldConfig),");
        expect(text).toContain('date: () => this.createCalendar({ ...field, type: \'date\' }),');
        expect(text).toContain('const calendar = config as CalendarFieldConfig;');
        expect(text).not.toContain('new TextField');
        expect(text).not.toContain('new CalendarField');
        // both interfaces used in isolated casts must be imported
        expect(text).toMatch(/\bTextFieldConfig\b.*from '@seniorsistemas\/angular-components\/dynamic-form'/s);
        expect(text).toMatch(/\bCalendarFieldConfig\b.*from '@seniorsistemas\/angular-components\/dynamic-form'/s);
        // the class body must still parse correctly — createCalendar must remain a sibling method,
        // not get swallowed by a stray unbalanced block from a botched arrow-function replacement
        expect(text).toContain('private createCalendar(config: any) {');
        expect((text.match(/private /g) ?? []).length).toBe(2);
    });
});
// ─── Field array annotation ────────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — field array annotation', () => {
    it('adiciona ": DynamicType[]" em const sem tipo com array de fields (strict)', async () => {
        const result = await runMigration({
            'src/app/lookup.ts': `
const defaultFields = [
    { name: "number", type: 'integer' },
    { name: "description", type: 'string' }
];
`,
        });
        const text = result.readText('src/app/lookup.ts');
        expect(text).toContain('const defaultFields: DynamicType[] = [');
        expect(text).toMatch(/import.*DynamicType.*from '@seniorsistemas\/angular-components\/dynamic-form'/);
    });
    it('NÃO modifica const já com tipo explícito', async () => {
        const original = `
const defaultFields: SomeType[] = [
    { name: "number", type: 'integer' }
];
`;
        const result = await runMigration({ 'src/app/lookup.ts': original });
        expect(result.readText('src/app/lookup.ts')).toBe(original);
    });
    it('NÃO modifica array com elementos sem type válido de field', async () => {
        const original = `
const routes = [
    { name: 'home', path: '/home' },
    { name: 'about', path: '/about' }
];
`;
        const result = await runMigration({ 'src/app/lookup.ts': original });
        expect(result.readText('src/app/lookup.ts')).toBe(original);
    });
    it('adiciona "as DynamicType[]" em arrow function sem return type com array body (strict)', async () => {
        const result = await runMigration({
            'src/app/lookup.ts': `
const getFields = ({ translateService }: { translateService: any }) => [
    { name: "aliquota", type: 'number' },
    { name: "vigencia", type: 'date' }
];
`,
        });
        const text = result.readText('src/app/lookup.ts');
        expect(text).toContain('] as DynamicType[]');
        expect(text).toMatch(/import.*DynamicType.*from '@seniorsistemas\/angular-components\/dynamic-form'/);
    });
    it('NÃO modifica arrow function com return type explícito', async () => {
        const original = `
const getFields = (): SomeType[] => [
    { name: "x", type: 'string' }
];
`;
        const result = await runMigration({ 'src/app/lookup.ts': original });
        expect(result.readText('src/app/lookup.ts')).toBe(original);
    });
    it('adiciona ": DynamicType[]" em array com spread objects + name/type (lenient)', async () => {
        const result = await runMigration({
            'src/app/lookup.ts': `
const lookupService: any = {};
const searchFields = [
    { ...lookupService.mountOptions({}), name: "origem" },
    { name: "aliquota", type: 'number' }
];
`,
        });
        const text = result.readText('src/app/lookup.ts');
        expect(text).toContain('const searchFields: DynamicType[] = [');
        expect(text).toMatch(/import.*DynamicType.*from '@seniorsistemas\/angular-components\/dynamic-form'/);
    });
    it('NÃO modifica array com spread sem nenhum elemento field-like', async () => {
        const original = `
const cfg = [
    { ...base, label: 'Home' },
    ...extra
];
`;
        const result = await runMigration({ 'src/app/lookup.ts': original });
        expect(result.readText('src/app/lookup.ts')).toBe(original);
    });
    it('adiciona "as DynamicType[]" em array com SpreadElement inline como argumento de função (lenient)', async () => {
        const result = await runMigration({
            'src/app/lookup.ts': `
function buildLookup(fields: any[]) {}
const getExtra = () => [{ name: 'x', type: 'string' }];
buildLookup([
    { name: "number", type: 'integer' },
    ...getExtra()
]);
`,
        });
        const text = result.readText('src/app/lookup.ts');
        expect(text).toContain('] as DynamicType[]');
        expect(text).toMatch(/import.*DynamicType.*from '@seniorsistemas\/angular-components\/dynamic-form'/);
    });
    it('adiciona import DynamicType quando ausente', async () => {
        const result = await runMigration({
            'src/app/lookup.ts': `
const f = [{ name: 'x', type: 'boolean' }];
`,
        });
        const text = result.readText('src/app/lookup.ts');
        expect(text).toMatch(/import.*DynamicType.*from '@seniorsistemas\/angular-components\/dynamic-form'/);
    });
});
// ─── Enum/select sem options ───────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — enum/select sem options', () => {
    it('NÃO modifica o código para { type: \'enum\' } sem options (apenas warn)', async () => {
        const original = `const f = [{ name: "imposto", type: 'enum' }];`;
        const result = await runMigration({ 'src/app/lookup.ts': original });
        // file must remain syntactically intact — type widening annotation may be added
        expect(result.readText('src/app/lookup.ts')).toContain("type: 'enum'");
    });
    it('NÃO modifica { type: \'enum\', options: [...] } (já tem options)', async () => {
        const original = `const f = [{ name: "x", type: 'enum', options: [{ value: 1, label: 'A' }] }];`;
        const result = await runMigration({ 'src/app/lookup.ts': original });
        expect(result.readText('src/app/lookup.ts')).toContain("options: [");
    });
    it('NÃO modifica { type: \'enum\', options: fn() } com função de options', async () => {
        const original = `
function getOpts() { return []; }
const f = [{ name: "x", type: 'enum', options: getOpts() }];
`;
        const result = await runMigration({ 'src/app/lookup.ts': original });
        expect(result.readText('src/app/lookup.ts')).toContain('options: getOpts()');
    });
});
// ─── FormField heritage ────────────────────────────────────────────────────────
describe('migrate-dynamic-form-v19 — FormField heritage warning/fix', () => {
    it('remove "extends FormField" de interface quando é o único extends', async () => {
        const result = await runMigration({
            'src/app/erp-lookups.ts': `
import { FormField } from '@seniorsistemas/angular-components/dynamic-form';
export interface FormFieldErpx extends FormField { maxLength?: number; }
`,
        });
        const text = result.readText('src/app/erp-lookups.ts');
        expect(text).toContain('export interface FormFieldErpx');
        expect(text).not.toContain('extends FormField');
        expect(text).not.toMatch(/import.*FormField/);
    });
    it('remove "FormField" de interface com múltiplos extends (mantém os outros)', async () => {
        const result = await runMigration({
            'src/app/erp-lookups.ts': `
import { FormField } from '@seniorsistemas/angular-components/dynamic-form';
export interface MyField extends FormField, OtherBase { extra?: string; }
`,
        });
        const text = result.readText('src/app/erp-lookups.ts');
        expect(text).toContain('extends OtherBase');
        expect(text).not.toContain('FormField');
    });
    it('mantém "extends FormField" em classe e não remove (apenas warn)', async () => {
        const result = await runMigration({
            'src/app/erp-lookups.ts': `
import { FormField } from '@seniorsistemas/angular-components/dynamic-form';
export class ErpLookupsFormField extends FormField {
    constructor(config: any) { super(config); }
}
`,
        });
        const text = result.readText('src/app/erp-lookups.ts');
        expect(text).toContain('class ErpLookupsFormField');
        expect(text).toContain('extends FormField');
    });
    it('arquivo com ConstructorParameters<typeof FormField> permanece sintaticamente íntegro', async () => {
        const result = await runMigration({
            'src/app/erp-lookups.ts': `
import { FormField } from '@seniorsistemas/angular-components/dynamic-form';
export class Foo extends FormField {
    constructor(...args: ConstructorParameters<typeof FormField>) { super(...args); }
}
`,
        });
        const text = result.readText('src/app/erp-lookups.ts');
        expect(text).toContain('ConstructorParameters');
        expect(text).toContain('class Foo');
    });
});
// ─── BaseFieldComponentConfig: @Input → input signal ─────────────────────────
describe('migrate-dynamic-form-v19 — BaseFieldComponentConfig: @Input → input signal', () => {
    it('converte @Input() field: Signal<CustomType> → field = input.required<CustomType>()', async () => {
        const result = await runMigration({
            'src/app/postalcode-field.component.ts': `
import { Component, Input, Signal } from '@angular/core';
import { FormControl } from '@angular/forms';
import { BaseFieldComponentConfig } from '@seniorsistemas/angular-components/dynamic-form';
import { PostalCodeLookupField } from './postalcode-lookup-field';

@Component({ template: '' })
export class PostalCodeLookupFieldComponent implements BaseFieldComponentConfig {
    @Input()
    public field: Signal<PostalCodeLookupField>;
    @Input()
    public formControl: Signal<FormControl>;
}
`,
        });
        const text = result.readText('src/app/postalcode-field.component.ts');
        expect(text).toContain('field = input.required<PostalCodeLookupField>()');
        expect(text).toContain('formControl = input<FormControl>()');
        expect(text).not.toContain('@Input()');
    });
    it('converte @Input() field: CustomType (sem Signal wrapper) → field = input.required<CustomType>()', async () => {
        const result = await runMigration({
            'src/app/postalcode-field.component.ts': `
import { Component, Input } from '@angular/core';
import { FormControl } from '@angular/forms';
import { BaseFieldComponentConfig } from '@seniorsistemas/angular-components/dynamic-form';
import { PostalCodeField } from './postalcode-field';

@Component({ template: '' })
export class PostalCodeFieldComponent implements BaseFieldComponentConfig {
    @Input()
    field: PostalCodeField;
    @Input()
    formControl: FormControl;
}
`,
        });
        const text = result.readText('src/app/postalcode-field.component.ts');
        expect(text).toContain('field = input.required<PostalCodeField>()');
        expect(text).toContain('formControl = input<FormControl>()');
    });
    it('NÃO converte @Input() em classe que NÃO implementa BaseFieldComponentConfig', async () => {
        const original = `
import { Component, Input } from '@angular/core';
import { BaseFieldComponentConfig } from '@seniorsistemas/angular-components/dynamic-form';

@Component({ template: '' })
export class SomeOtherComponent {
    @Input()
    field: string;
}
`;
        const result = await runMigration({ 'src/app/comp.ts': original });
        expect(result.readText('src/app/comp.ts')).toContain('@Input()');
        expect(result.readText('src/app/comp.ts')).not.toContain('input.required');
    });
    it('preserva outros @Input() que não são field/formControl', async () => {
        const result = await runMigration({
            'src/app/postalcode-field.component.ts': `
import { Component, Input, Signal } from '@angular/core';
import { FormControl } from '@angular/forms';
import { BaseFieldComponentConfig } from '@seniorsistemas/angular-components/dynamic-form';

@Component({ template: '' })
export class MyFieldComponent implements BaseFieldComponentConfig {
    @Input()
    field: Signal<any>;
    @Input()
    formControl: Signal<FormControl>;
    @Input()
    label: string;
}
`,
        });
        const text = result.readText('src/app/postalcode-field.component.ts');
        expect(text).toContain('field = input.required<any>()');
        expect(text).toContain('formControl = input<FormControl>()');
        expect(text).toContain('@Input()');
        expect(text).toContain('label: string');
    });
    it('adiciona import { input } a @angular/core', async () => {
        const result = await runMigration({
            'src/app/postalcode-field.component.ts': `
import { Component, Input } from '@angular/core';
import { BaseFieldComponentConfig } from '@seniorsistemas/angular-components/dynamic-form';

@Component({ template: '' })
export class MyFieldComponent implements BaseFieldComponentConfig {
    @Input()
    field: any;
    @Input()
    formControl: any;
}
`,
        });
        const text = result.readText('src/app/postalcode-field.component.ts');
        expect(text).toMatch(/import\s*\{[^}]*\binput\b[^}]*\}\s*from\s*'@angular\/core'/);
    });
    it('remove Input de @angular/core quando não há mais @Input() no arquivo', async () => {
        const result = await runMigration({
            'src/app/postalcode-field.component.ts': `
import { Component, Input } from '@angular/core';
import { BaseFieldComponentConfig } from '@seniorsistemas/angular-components/dynamic-form';

@Component({ template: '' })
export class MyFieldComponent implements BaseFieldComponentConfig {
    @Input()
    field: any;
    @Input()
    formControl: any;
}
`,
        });
        const text = result.readText('src/app/postalcode-field.component.ts');
        expect(text).not.toMatch(/import\s*\{[^}]*\bInput\b[^}]*\}\s*from\s*'@angular\/core'/);
    });
    it('mantém Input em @angular/core quando há outros @Input() no arquivo', async () => {
        const result = await runMigration({
            'src/app/postalcode-field.component.ts': `
import { Component, Input } from '@angular/core';
import { BaseFieldComponentConfig } from '@seniorsistemas/angular-components/dynamic-form';

@Component({ template: '' })
export class MyFieldComponent implements BaseFieldComponentConfig {
    @Input()
    field: any;
    @Input()
    formControl: any;
    @Input()
    label: string;
}
`,
        });
        const text = result.readText('src/app/postalcode-field.component.ts');
        expect(text).toMatch(/import\s*\{[^}]*\bInput\b[^}]*\}\s*from\s*'@angular\/core'/);
    });
});
//# sourceMappingURL=index.test.js.map