import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, SimpleChanges, ChangeDetectorRef, ViewChild, ViewContainerRef, ComponentRef, ViewChildren, QueryList, AfterViewInit, NgZone } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
  FormGroup,
  FormBuilder,
  ReactiveFormsModule,
  Validators,
  AbstractControl,
  ValidationErrors,
  FormControl,
  FormsModule 
} from '@angular/forms';
import { CustomInputComponent } from '../inputs/custom-input/custom-input.component';
import { ImageUploaderComponent } from '../image-uploader/image-uploader.component';
import { PdfUploaderComponent } from '../pdf-uploader/pdf-uploader.component';
import { CheckboxListComponent } from '../checkbox-list/checkbox-list.component';
import { CheckboxGroupedComponent } from '../checkbox-grouped/checkbox-grouped.component';
import { MultiSelectComponent } from '../multi-select/multi-select.component';
import { SlideToggleComponent } from '../inputs/slide-toggle/slide-toggle.component';
import { RemoteSelectComponent } from '../remote-select/remote-select.component';
import { KilometerInputComponent } from '../inputs/kilometer-input/kilometer-input.component';
import { ColorInputComponent } from '../inputs/color-input/color-input.component';
import { CurrencyInputComponent } from '../inputs/currency-input/currency-input.component';
import { TextareaInputComponent } from '../inputs/textarea-input/textarea-input.component';
import { PasswordInputComponent } from '../inputs/password-input/password-input.component';
import { ImageUploadService } from '../image-uploader/image-uploader.service';
import { HttpClient } from '@angular/common/http';
import { Observable, of, catchError, tap, Subscription, forkJoin, map, switchMap, take, shareReplay } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { TabItem } from '../tab-system/interfaces/tab-system.interface';
import { Logger } from '../../services/logger/logger.service';
import { ImagesIncludeId, DataUrlFile } from '../../interfaces/image.interface';
import { ImagesIncludeIdExtended, ImageApiResponse } from '../image-uploader/image-uploader.component';
import { 
  TabFormField, 
  TabFormConfig, 
  TabFormData, 
  TabFormImageData, 
  TabFormValidationEvent,
  SubTabConfig
} from '../../interfaces/generic-tab-form.interface';
import { CepService } from '../address-form/cep.service';
import { AddressFormComponent, AddressData } from '../address-form/address-form.component';
import { DynamicComponentResolverService } from '../../services/dynamic-component-resolver.service';
// import { RouteStopsComponent } from '../../../domain/routes/route-stops/route-stops.component';

@Component({
  selector: 'app-generic-tab-form',
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    FormsModule,
    CustomInputComponent,
    ImageUploaderComponent,
    PdfUploaderComponent,
    CheckboxListComponent,
    CheckboxGroupedComponent,
    MultiSelectComponent,
    SlideToggleComponent,
    RemoteSelectComponent,
    KilometerInputComponent,
    ColorInputComponent,
    CurrencyInputComponent,
    TextareaInputComponent,
    PasswordInputComponent,
    // RouteStopsComponent
  ],
  templateUrl: './generic-tab-form.component.html',
  styleUrls: ['./generic-tab-form.component.scss']
})
export class GenericTabFormComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
  
  // 🎨 Mapeamento de domínios para imagens padrão
  private readonly DOMAIN_DEFAULT_IMAGES: { [domain: string]: string } = {
    // 👤 Pessoas
    'driver': 'assets/imagens/driver.placeholder.svg',
    'user': 'assets/imagens/user.placeholder.svg',
    'customer': 'assets/imagens/user.placeholder.svg', // Usa user como fallback
    
    // 🚗 Veículos  
    'vehicle': 'assets/imagens/vehicle.placeholder.svg',
    'car': 'assets/imagens/vehicle.placeholder.svg',
    
    // 🏢 Empresas
    'company': 'assets/imagens/company.placeholder.svg',
    'supplier': 'assets/imagens/company.placeholder.svg',
    
    // 📦 Produtos
    'product': 'assets/imagens/product.placeholder.svg',
    
    // 🛣️ Rotas
    'route': 'assets/imagens/route.placeholder.svg',
    
    // 💰 Financeiro
    'account-payable': 'assets/imagens/financial.placeholder.svg',
    'financial-categories': 'assets/imagens/financial.placeholder.svg',
    
    // 🚛 Outros
    'fines': 'assets/imagens/fine.placeholder.svg',
    'tollparking': 'assets/imagens/toll.placeholder.svg',
    'fuelcontroll': 'assets/imagens/fuel.placeholder.svg',
    'devicetracker': 'assets/imagens/device.placeholder.svg'
  };
  @Input() config!: TabFormConfig;
  @Input() initialData?: any;
  @Input() tabItem!: TabItem;
  @Input() isLoading = false;
  
  @Output() formSubmit = new EventEmitter<any>();
  @Output() formCancel = new EventEmitter<void>();
  @Output() formChange = new EventEmitter<{ valid: boolean; data: any }>();

  form: FormGroup;
  isFormDirty = false;
  isSubmitting = false;
  isNewItem = false;
  isSavedSuccessfully = false;
  
  imageFieldData: {
    [key: string]: { images: ImagesIncludeId[]; files: File[] };
  } = {};
  imagePreviewsMap: { [key: string]: Observable<ImagesIncludeId[]> } = {};
  
  // ========================================
  // 📄 PROPRIEDADES PDF UPLOADER
  // ========================================
  
  pdfFieldData: {
    [key: string]: { attachments: any[]; files: File[] };
  } = {};
  pdfAttachmentsMap: { [key: string]: Observable<any[]> } = {};
  
  // Mapa para armazenar opções carregadas via API
  apiOptionsMap: { [key: string]: { value: any; label: string }[] } = {};

  // 🚀 CACHE ROBUSTO: Configurações processadas de remote-select IMUTÁVEIS
  private remoteConfigCache = new Map<string, any>();
  private stableRemoteConfigs = new Map<string, any>();

  private logger: Logger;
  private formSubscriptions: Subscription[] = [];
  selectedSubTab: string = 'dados';

  // Estado de colapso do side card para mobile
  sideCardCollapsed = false;

  // 🎯 CONTROLE SIMPLES DE EDIÇÃO
  isEditMode = false;

  // 🚨 PROTEÇÃO ANTI-INTERFERÊNCIA: Evita que operações internas afetem remote-select
  private isPreservingFormState = false;

  // 🚀 SISTEMA DINÂMICO DE COMPONENTES
  @ViewChild('dynamicComponentOutlet', { read: ViewContainerRef, static: false }) dynamicComponentOutlet!: ViewContainerRef;
  private dynamicComponentRefs: Map<string, ComponentRef<any>> = new Map();

  /**
   * 🚨 PROTEÇÃO ANTI-INTERFERÊNCIA
   * Executa uma operação preservando os valores dos remote-select
   */
  private preserveFormStateDuring(operation: () => void): void {
    console.log(`🛡️ [PROTEÇÃO] Iniciando operação com proteção de estado`);
    this.isPreservingFormState = true;
    
    try {
      operation();
      console.log(`✅ [PROTEÇÃO] Operação concluída com sucesso`);
    } catch (error) {
      console.error(`❌ [PROTEÇÃO] Erro durante operação protegida:`, error);
    } finally {
      // Garantir que sempre limpe a flag, mesmo em caso de erro
      setTimeout(() => {
        this.isPreservingFormState = false;
        console.log(`🔓 [PROTEÇÃO] Estado de proteção removido`);
      }, 100);
    }
  }

  constructor(
    private fb: FormBuilder,
    private imageUploadService: ImageUploadService,
    private http: HttpClient,
    private cepService: CepService,
    private cdr: ChangeDetectorRef,
    private dynamicComponentResolver: DynamicComponentResolverService,
    private ngZone: NgZone
  ) {
    this.logger = new Logger('GenericTabForm');
    this.form = this.fb.group({});
  }

  ngOnInit() {
    this.isNewItem = this.initialData?.id === 'new' || !this.initialData?.id;
    
    // Para itens novos, ativar modo de edição automaticamente
    if (this.isNewItem) {
      this.isEditMode = true;
    }
    
    // 🎯 LIMPAR CACHE: Garantir configs frescos para nova inicialização
    this.stableRemoteConfigs.clear();
    
    this.initializeSideCardState();
    this.initializeDefaultSubTab();
    this.initForm();
    
    // ✅ REATIVADO: Operações de imagem necessárias para carregar fotos existentes
    this.initializeImageConfiguration();
    this.initializeImagePreviews();
    
    // ✅ REATIVADO: Atualização do side card
    this.updateSideCardImage();
    
    // ✅ REATIVADO: Setup de change detection para modo de edição
    if (this.isEditMode) {
      setTimeout(() => this.setupFormChangeDetection(), 0);
    }
    
    // 🎯 NOVO: Escutar mudanças nos campos que afetam cálculos
    this.setupComputedFieldsListener();
  }

  ngOnDestroy() {
    this.formSubscriptions.forEach(sub => sub.unsubscribe());
    this.formSubscriptions = [];
    
    // Limpar componentes dinâmicos
    this.destroyDynamicComponents();
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log(`🔍 [GenericTabForm-DEBUG] ngOnChanges chamado:`, {
      changes: Object.keys(changes).map(key => ({
        key,
        isFirstChange: changes[key].firstChange,
        currentValue: changes[key].currentValue,
        previousValue: changes[key].previousValue
      })),
      isEditMode: this.isEditMode,
      isPreservingFormState: this.isPreservingFormState,
      stackTrace: new Error().stack?.split('\n').slice(1, 4).join('\n')
    });
    
    // 🚨 PROTEÇÃO CRÍTICA: NUNCA recriar formulário se estiver em modo de edição
    if (this.isEditMode) {
      console.log(`🛡️ [GenericTabForm-DEBUG] Bloqueado ngOnChanges - em modo de edição`);
      return;
    }
    
    // 🚨 PROTEÇÃO EXTRA: Não recriar formulário durante operações críticas
    if (this.isPreservingFormState) {
      console.log(`🛡️ [GenericTabForm-DEBUG] Bloqueado ngOnChanges - preservando estado do formulário`);
      return;
    }
    
    // 🎯 IMPORTANTE: Só recriar formulário se não for a primeira mudança
    // (primeira mudança é a inicialização normal)
    const hasNonFirstChanges = Object.keys(changes).some(key => !changes[key].firstChange);
    
    if (hasNonFirstChanges) {
      console.log(`🔄 [GenericTabForm-DEBUG] Recriando formulário devido a mudanças não-iniciais`);
      // 🎯 LIMPAR CACHE: Configurações podem ter mudado
      this.stableRemoteConfigs.clear();
      this.preserveFormStateDuring(() => this.initForm());
    }
  }

  ngAfterViewInit() {
    console.log(`🔄 [INIT] ngAfterViewInit - Sub-aba selecionada: ${this.selectedSubTab}`);
    
    // 🔧 REATIVADO: Componentes dinâmicos necessários para subTabs (Localização, Paradas)
    // MAS sem Zone.js complexo que pode interferir no remote-select
    setTimeout(() => {
      const subTab = this.getSubTabConfig(this.selectedSubTab);
      if (subTab && this.hasDynamicComponent(subTab)) {
        console.log(`🎯 [INIT] Criando componente dinâmico simplificado: ${this.selectedSubTab}`);
        this.createDynamicComponentForSubTab(subTab);
      } else {
        console.log(`ℹ️ [INIT] Nenhum componente dinâmico para: ${this.selectedSubTab}`);
      }
    }, 100);
  }

  private initForm() {
    console.log(`🔍 [GenericTabForm-DEBUG] initForm chamado:`, {
      isEditMode: this.isEditMode,
      hasInitialData: !!this.initialData,
      formExists: !!this.form,
      stackTrace: new Error().stack?.split('\n').slice(1, 4).join('\n')
    });
    
    // 🚨 PROTEÇÃO EXTRA: Preservar estado de edição se estiver ativo
    const wasInEditMode = this.isEditMode;
    
    const formGroup: { [key: string]: FormControl } = {};
    
    // 🎯 NOVO: Coletar todos os campos de todas as sub-abas
    const allFields = this.getAllFieldsFromSubTabs();
    
    allFields.forEach(field => {
      const validators = [];
      
      if (field.required) {
        validators.push(Validators.required);
      }
      
      if (field.type === 'email') {
        validators.push(Validators.email);
      }
      
      if (field.validators) {
        validators.push(...field.validators);
      }
      
      // 🚨 CORREÇÃO: Só aplicar validador customizado em campos obrigatórios
      if (field.type === 'select' && field.options && field.required) {
        validators.push(this.createOptionValidator(field.options, field));
      }

      let initialValue = this.getInitialValue(field);
      
      // 🎯 NOVO: Se o campo tem compute, calcular valor inicial
      if (field.compute && this.initialData) {
        try {
          const computedValue = field.compute(this.initialData);
          if (computedValue !== undefined && computedValue !== null) {
            initialValue = computedValue;
          }
        } catch (error) {
          console.warn(`⚠️ Erro ao calcular campo ${field.key}:`, error);
        }
      }
      
      // 🎯 CONTROLE DE EDIÇÃO: Usar estado preservado se estava em edição
      // 🔒 READONLY: Campos readOnly nunca são desabilitados, apenas não editáveis
      const shouldDisable = field.disabled === true || (field.readOnly !== true && !this.isNewItem && !wasInEditMode);
      
      if (shouldDisable) {
        formGroup[field.key] = new FormControl(
          { value: initialValue, disabled: true },
          validators
        );
      } else {
        formGroup[field.key] = new FormControl(
          initialValue,
          validators
        );
      }
    });

    this.form = this.fb.group(formGroup);
    
    // 🚨 RESTAURAR estado de edição se estava ativo
    if (wasInEditMode) {
      this.isEditMode = true;
    }

    // 🎯 NOVO: Configurar listeners para campos condicionais
    this.setupConditionalFieldListeners();
  }

  /**
   * 🎯 NOVO: Coleta todos os campos de todas as sub-abas
   */
  private getAllFieldsFromSubTabs(): TabFormField[] {
    const allFields: TabFormField[] = [];
    
    // ✅ Adicionar campos globais (se houver) - Necessário para FormGroup
    if (this.config.fields) {
      allFields.push(...this.config.fields);
    }
    
    // Adicionar campos de cada sub-aba
    if (this.config.subTabs) {
      this.config.subTabs.forEach(subTab => {
        if (subTab.fields && subTab.enabled !== false) {
          allFields.push(...subTab.fields);
        }
      });
    }
    
    return allFields;
  }

  /**
   * 🎯 NOVO: Obtém campos específicos de uma sub-aba
   */
  getSubTabFields(subTabId: string): TabFormField[] {
    const subTab = this.config.subTabs?.find(tab => tab.id === subTabId);
    return subTab?.fields || [];
  }

  /**
   * 🎯 CORREÇÃO CRÍTICA: Config ESTÁVEL para remote-select
   * Evita recriação constante que causa limpeza dos campos
   */
  getProcessedRemoteConfig(field: TabFormField): any {
    if (field.type !== 'remote-select' || !field.remoteConfig) {
      return field.remoteConfig;
    }

    // 🚀 CHAVE SIMPLES E ESTÁVEL: Baseada apenas no campo + dados iniciais
    const stableKey = field.key;
    
    // 🚀 RETORNO IMEDIATO: Se já processamos este campo, retornar MESMO objeto
    if (this.stableRemoteConfigs.has(stableKey)) {
      return this.stableRemoteConfigs.get(stableKey);
    }

    // 🎯 CRIAR UMA ÚNICA VEZ: Config com initialValue se disponível
    const stableConfig = { 
      ...field.remoteConfig,
      // 🚀 ADICIONAR initialValue se temos os dados
      initialValue: this.createInitialValueForField(field)
    };
    
    // 🎯 ARMAZENAR PERMANENTEMENTE: Mesmo objeto será retornado sempre
    this.stableRemoteConfigs.set(stableKey, stableConfig);
    
    return stableConfig;
  }

  /**
   * 🎯 HELPER: Cria initialValue para campo específico
   * ✨ NOVO: Busca automática quando label não existe nos dados
   */
  private createInitialValueForField(field: TabFormField): any {
    if (!this.initialData) {
      return undefined;
    }

    const fieldValue = this.initialData[field.key];
    if (!fieldValue) {
      return undefined;
    }

    const labelField = this.getCorrespondingLabelField(field.key, field);
    const labelValue = this.initialData[labelField];
    
    // ✅ CASO 1: Já temos o label nos dados iniciais
    if (labelValue) {
      console.log(`🎯 [INITIAL_VALUE] Campo ${field.key}: usando label existente (${labelField}: ${labelValue})`);
      return {
        id: fieldValue,
        label: labelValue
      };
    }
    
    // 🚀 CASO 2: Não temos o label - remote-select vai buscar via API
    console.log(`🔍 [INITIAL_VALUE] Campo ${field.key}: label não encontrado, remote-select fará busca via API`);
    return undefined;
  }

  /**
   * 🎯 HELPER: Mapeia campo ID para campo NOME correspondente
   * Ex: company_id → company_name, driver_id → driver_name
   * 
   * ✨ NOVO: Suporte a configuração personalizada via field.labelField
   * 
   * @param fieldKey - Nome do campo (ex: 'company_id')
   * @param field - Configuração do campo (opcional, para labelField customizado)
   * @returns Nome do campo de label correspondente
   */
  private getCorrespondingLabelField(fieldKey: string, field?: TabFormField): string {
    // 🎯 PRIORIDADE 1: Configuração personalizada no campo
    if (field?.labelField) {
      console.log(`🎯 [MAPPING] Campo ${fieldKey} usando labelField customizado: ${field.labelField}`);
      return field.labelField;
    }
    
    // 🎯 PRIORIDADE 2: Casos específicos que fogem do padrão
    const specificMappings: { [key: string]: string } = {
      'vehicleId': 'licensePlate',
      'personId': 'personName', 
      'supplierId': 'supplierName',
      'companyId': 'companyName',
      'vehicle_id': 'vehicle_license_plate', // Para casos snake_case legados
      'route_id': 'route_name',
      // Adicionar outros casos conforme necessário
    };
    
    if (specificMappings[fieldKey]) {
      console.log(`🎯 [MAPPING] Campo ${fieldKey} usando mapeamento específico: ${specificMappings[fieldKey]}`);
      return specificMappings[fieldKey];
    }
    
    // 🎯 PRIORIDADE 3: Mapeamento padrão snake_case
    if (fieldKey.endsWith('_id')) {
      const defaultMapping = fieldKey.replace('_id', '_name');
      console.log(`🎯 [MAPPING] Campo ${fieldKey} usando mapeamento padrão snake_case: ${defaultMapping}`);
      return defaultMapping;
    }
    
    // 🎯 FALLBACK: Adicionar '_name' ao final
    const fallbackMapping = `${fieldKey}_name`;
    console.log(`🎯 [MAPPING] Campo ${fieldKey} usando fallback: ${fallbackMapping}`);
    return fallbackMapping;
  }



  private createOptionValidator(options: { value: any; label: string }[], field?: TabFormField) {
    return (control: AbstractControl): ValidationErrors | null => {
      console.log(`🚨 [VALIDATOR] Campo ${field?.key} sendo validado:`, {
        controlValue: control.value,
        controlValueType: typeof control.value,
        controlValueStringified: JSON.stringify(control.value),
        isRequired: field?.required,
        hasReturnObjectSelected: field?.returnObjectSelected,
        optionsCount: options.length
      });
      
      // 1. Valor vazio é sempre válido
      if (!control.value) {
        console.log(`✅ [VALIDATOR] Campo ${field?.key} - valor vazio, válido`);
        return null;
      }
      
      // 2. Para campos com returnObjectSelected
      if (field?.returnObjectSelected) {
        console.log(`🔄 [VALIDATOR] Campo ${field?.key} - validando com returnObjectSelected`);
        return this.validateObjectField(control.value, options);
      }
      
      // 3. Para campos normais
      console.log(`🔄 [VALIDATOR] Campo ${field?.key} - validando como campo normal`);
      return this.validateNormalField(control.value, options);
    };
  }

  /**
   * 🎯 VALIDAÇÃO PARA CAMPOS COM returnObjectSelected
   * Lógica limpa para comparar objetos e strings
   */
  private validateObjectField(controlValue: any, options: { value: any; label: string }[]): ValidationErrors | null {
    console.log(`🔍 [DEBUG] validateObjectField iniciando:`, {
      controlValue,
      controlType: typeof controlValue,
      controlValueStringified: JSON.stringify(controlValue),
      optionsCount: options.length
    });
    
    const isValid = options.some((option, index) => {
      console.log(`🔍 [DEBUG] Testando opção ${index}:`, {
        optionValue: option.value,
        optionLabel: option.label,
        optionValueType: typeof option.value,
        optionValueStringified: JSON.stringify(option.value)
      });
      
      // 🚨 CORREÇÃO: Verificar se controlValue é realmente um objeto mesmo que typeof diga que é string
      const isControlValueObject = controlValue && 
        (typeof controlValue === 'object' || 
         (typeof controlValue === 'string' && controlValue.toString() === '[object Object]'));
      
      const isOptionValueObject = option.value && typeof option.value === 'object';
      
      console.log(`🔍 [DEBUG] Detecção de tipos:`, {
        isControlValueObject,
        isOptionValueObject,
        controlValueToString: controlValue?.toString?.()
      });
      
      // Caso 1: Ambos são objetos - comparar propriedades importantes
      if (isControlValueObject && isOptionValueObject) {
        console.log(`🔍 [DEBUG] Caso 1 - Objeto vs Objeto (corrigido)`);
        const result = this.compareObjects(controlValue, option.value);
        console.log(`🔍 [DEBUG] compareObjects resultado: ${result}`);
        return result;
      }
      
      // Caso 2: Control é string, Option é objeto - extrair nome do objeto
      if (!isControlValueObject && isOptionValueObject) {
        console.log(`🔍 [DEBUG] Caso 2 - String vs Objeto`);
        const result = this.compareStringWithObject(controlValue, option.value, option.label);
        console.log(`🔍 [DEBUG] compareStringWithObject resultado: ${result}`);
        return result;
      }
      
      // Caso 3: Control é objeto, Option é string - extrair nome do objeto
      if (isControlValueObject && !isOptionValueObject) {
        console.log(`🔍 [DEBUG] Caso 3 - Objeto vs String`);
        const result = this.compareObjectWithString(controlValue, option.value, option.label);
        console.log(`🔍 [DEBUG] compareObjectWithString resultado: ${result}`);
        return result;
      }
      
      // Caso 4: Ambos são strings - comparação direta
      console.log(`🔍 [DEBUG] Caso 4 - String vs String`);
      const result = controlValue === option.value;
      console.log(`🔍 [DEBUG] comparação string direta resultado: ${result}`);
      return result;
    });

    console.log(`🔍 [DEBUG] Resultado FINAL da validação: ${isValid ? 'VÁLIDO' : 'INVÁLIDO'}`);
    return isValid ? null : { invalidOption: true };
  }

  /**
   * 🎯 VALIDAÇÃO PARA CAMPOS NORMAIS
   * Lógica simples para campos sem returnObjectSelected
   */
  private validateNormalField(controlValue: any, options: { value: any; label: string }[]): ValidationErrors | null {
    // Extrair valor comparável
    const valueToCheck = typeof controlValue === 'object' && controlValue?.value !== undefined 
      ? controlValue.value 
      : controlValue;

    const isValid = options.some((option) => option.value === valueToCheck);
    return isValid ? null : { invalidOption: true };
  }

  /**
   * 🔧 UTILITÁRIO: Comparar dois objetos
   * Comparação genérica e agnóstica para qualquer tipo de objeto
   */
  private compareObjects(obj1: any, obj2: any): boolean {
    console.log(`🔍 [COMPARE_OBJECTS] Comparando:`, {
      obj1,
      obj2,
      obj1Type: typeof obj1,
      obj2Type: typeof obj2
    });
    
    if (!obj1 || !obj2) {
      console.log(`🔍 [COMPARE_OBJECTS] Um dos objetos é null/undefined`);
      return false;
    }
    
    // Se são exatamente o mesmo objeto (referência)
    if (obj1 === obj2) {
      console.log(`🔍 [COMPARE_OBJECTS] Mesma referência - IGUAIS`);
      return true;
    }
    
    // Obter todas as chaves de ambos os objetos
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);
    
    console.log(`🔍 [COMPARE_OBJECTS] Chaves:`, {
      keys1,
      keys2,
      keys1Length: keys1.length,
      keys2Length: keys2.length
    });
    
    // Se têm número diferente de propriedades, são diferentes
    if (keys1.length !== keys2.length) {
      console.log(`🔍 [COMPARE_OBJECTS] Número de propriedades diferente - DIFERENTES`);
      return false;
    }
    
    // Comparar cada propriedade
    for (const key of keys1) {
      // Se obj2 não tem a propriedade, são diferentes
      if (!(key in obj2)) {
        console.log(`🔍 [COMPARE_OBJECTS] obj2 não tem a propriedade '${key}' - DIFERENTES`);
        return false;
      }
      
      // Normalizar valores para comparação
      const value1 = this.normalizeForComparison(obj1[key]);
      const value2 = this.normalizeForComparison(obj2[key]);
      
      console.log(`🔍 [COMPARE_OBJECTS] Comparando propriedade '${key}':`, {
        original1: obj1[key],
        original2: obj2[key],
        normalized1: value1,
        normalized2: value2,
        equal: value1 === value2
      });
      
      // Se os valores normalizados são diferentes, objetos são diferentes
      if (value1 !== value2) {
        console.log(`🔍 [COMPARE_OBJECTS] Propriedade '${key}' diferente - OBJETOS DIFERENTES`);
        return false;
      }
    }
    
    console.log(`🔍 [COMPARE_OBJECTS] Todas as propriedades iguais - OBJETOS IGUAIS`);
    return true;
  }

  /**
   * 🔧 UTILITÁRIO: Normalizar valor para comparação
   * Converte qualquer valor para formato comparável
   */
  private normalizeForComparison(value: any): string {
    if (value === null || value === undefined) {
      return '';
    }
    
    // Se for um objeto, extrair todos os valores e concatenar
    if (typeof value === 'object') {
      const values = Object.values(value).map(v => 
        v !== null && v !== undefined ? v.toString() : ''
      );
      return values.join(' ').toLowerCase().trim();
    }
    
    return value.toString().toLowerCase().trim();
  }

  /**
   * 🔧 UTILITÁRIO: Comparar string com objeto
   * String no control, objeto na option
   */
  private compareStringWithObject(controlString: string, optionObject: any, optionLabel?: string): boolean {
    if (!controlString || !optionObject) return false;
    
    // 🚨 CORREÇÃO: Se controlString é na verdade um objeto, usar compareObjects
    if (typeof controlString === 'object') {
      console.log(`🔍 [compareStringWithObject] controlString é objeto, usando compareObjects`);
      return this.compareObjects(controlString, optionObject);
    }
    
    const controlNormalized = this.normalizeForComparison(controlString);
    
    console.log(`🔍 [compareStringWithObject] Comparando string com objeto:`, {
      controlString,
      controlNormalized,
      optionObject,
      optionLabel
    });
    
    // Comparar string com cada propriedade do objeto
    for (const key in optionObject) {
      if (optionObject.hasOwnProperty(key)) {
        const objectValue = this.normalizeForComparison(optionObject[key]);
        console.log(`🔍 [compareStringWithObject] Testando propriedade '${key}':`, {
          objectValue,
          match: objectValue === controlNormalized
        });
        if (objectValue === controlNormalized) {
          return true;
        }
      }
    }
    
    // Comparar com label da option
    if (optionLabel) {
      const labelNormalized = this.normalizeForComparison(optionLabel);
      console.log(`🔍 [compareStringWithObject] Testando label:`, {
        optionLabel,
        labelNormalized,
        match: labelNormalized === controlNormalized
      });
      if (labelNormalized === controlNormalized) {
        return true;
      }
    }
    
    return false;
  }

  /**
   * 🔧 UTILITÁRIO: Comparar objeto com string
   * Objeto no control, string na option
   */
  private compareObjectWithString(controlObject: any, optionString: string, optionLabel?: string): boolean {
    if (!controlObject || !optionString) return false;
    
    const optionNormalized = this.normalizeForComparison(optionString);
    const labelNormalized = optionLabel ? this.normalizeForComparison(optionLabel) : '';
    
    // Comparar cada propriedade do objeto com a string da option
    for (const key in controlObject) {
      if (controlObject.hasOwnProperty(key)) {
        const objectValue = this.normalizeForComparison(controlObject[key]);
        if (objectValue === optionNormalized || objectValue === labelNormalized) {
          return true;
        }
      }
    }
    
    return false;
  }

  /**
   * 🎯 NOVO: Configura campos de imagem com dados existentes
   */
  private initializeImageConfiguration() {
    const allFields = this.getAllFieldsFromSubTabs();
    
    console.log('🔍 [DEBUG] initializeImageConfiguration:', {
      allFields: allFields.filter(f => f.type === 'send-image').map(f => ({ key: f.key, type: f.type })),
      initialData: this.initialData,
      hasInitialData: !!this.initialData
    });
    
    allFields
      .filter((field) => field.type === 'send-image')
      .forEach((field) => {
        // 🎯 CONFIGURAR: Preencher existingImages com dados do item atual
        if (field.imageConfiguration) {
          const existingImageIds = this.getExistingImagesForField(field);
          console.log(`🔍 [DEBUG] Campo ${field.key}:`, {
            existingImageIds,
            isArray: Array.isArray(existingImageIds),
            length: Array.isArray(existingImageIds) ? existingImageIds.length : 0
          });
          
          if (Array.isArray(existingImageIds)) {
            field.imageConfiguration.existingImages = existingImageIds;
            console.log(`✅ [DEBUG] Campo ${field.key} configurado com ${existingImageIds.length} imagens`);
          }
        }
      });

    // ========================================
    // 📄 INICIALIZAR CAMPOS PDF
    // ========================================
    
    allFields
      .filter((field) => field.type === 'send-pdf')
      .forEach((field) => {
        // 🎯 CONFIGURAR: Preencher existingDocuments com dados do item atual
        if (field.pdfConfiguration) {
          const existingAttachments = this.getExistingAttachmentsForField(field);
          console.log(`📄 [DEBUG] Campo PDF ${field.key}:`, {
            existingAttachments,
            isArray: Array.isArray(existingAttachments),
            length: Array.isArray(existingAttachments) ? existingAttachments.length : 0
          });
          
          if (Array.isArray(existingAttachments)) {
            field.pdfConfiguration.existingDocuments = existingAttachments;
            console.log(`✅ [DEBUG] Campo PDF ${field.key} configurado com ${existingAttachments.length} documentos`);
            
            // Inicializar os dados PDF
            this.initializePdfData(field.key, existingAttachments);
          }
        }
      });
  }

  /**
   * 🎯 NOVO: Obtém imagens existentes para um campo específico
   * Suporta tanto dataBinding quanto requiredFields
   */
  private getExistingImagesForField(field: TabFormField): number[] {
    // 🎯 PRIORIDADE 1: Data Binding (nova abordagem)
    const subTab = this.getSubTabForField(field);
    if (subTab?.dataBinding?.getInitialData) {
      const initialData = subTab.dataBinding.getInitialData();
      if (initialData instanceof Observable) {
        // Observable - será tratado assincronamente no initializeImagePreviews
        return [];
      } else {
        // Array direto
        return Array.isArray(initialData) ? initialData : [];
      }
    }
    
    // 🎯 PRIORIDADE 2: Required Fields (abordagem atual)
    if (subTab?.requiredFields && subTab.requiredFields.length > 0 && this.initialData) {
      const fieldKey = subTab.requiredFields[0]; // Primeiro campo como referência
      const formValue = this.initialData[fieldKey];
      return Array.isArray(formValue) ? formValue : [];
    }
    
    // 🎯 PRIORIDADE 3: Campo direto do initialData
    if (this.initialData && this.initialData[field.key]) {
      const fieldValue = this.initialData[field.key];
      return Array.isArray(fieldValue) ? fieldValue : [];
    }
    
    // 🎯 FALLBACK: Campo vazio
    return [];
  }

  /**
   * 📄 NOVO: Obtém attachments existentes para um campo específico de PDF
   * Suporta tanto dataBinding quanto requiredFields
   */
  private getExistingAttachmentsForField(field: TabFormField): any[] {
    // 🎯 PRIORIDADE 1: Data Binding (nova abordagem)
    const subTab = this.getSubTabForField(field);
    if (subTab?.dataBinding?.getInitialData) {
      const initialData = subTab.dataBinding.getInitialData();
      if (initialData instanceof Observable) {
        // Observable - será tratado assincronamente
        return [];
      } else {
        // Array direto
        return Array.isArray(initialData) ? initialData : [];
      }
    }
    
    // 🎯 PRIORIDADE 2: Required Fields (abordagem atual)
    if (subTab?.requiredFields && subTab.requiredFields.length > 0 && this.initialData) {
      const fieldKey = subTab.requiredFields[0]; // Primeiro campo como referência
      const formValue = this.initialData[fieldKey];
      return Array.isArray(formValue) ? formValue : [];
    }
    
    // 🎯 PRIORIDADE 3: Campo direto do initialData
    if (this.initialData && this.initialData[field.key]) {
      const fieldValue = this.initialData[field.key];
      return Array.isArray(fieldValue) ? fieldValue : [];
    }
    
    // 🎯 FALLBACK: Campo vazio
    return [];
  }

  /**
   * 🎯 HELPER: Obtém a sub-aba que contém o campo especificado
   */
  private getSubTabForField(field: TabFormField): SubTabConfig | undefined {
    if (!this.config.subTabs) return undefined;
    
    return this.config.subTabs.find(subTab => 
      subTab.fields?.some(f => f.key === field.key)
    );
  }

  private initializeImagePreviews() {
    // 🎯 CORREÇÃO: Verificar campos de TODAS as sub-abas, não apenas campos globais
    const allFields = this.getAllFieldsFromSubTabs();
    
    console.log('🔍 [DEBUG] initializeImagePreviews:', {
      allFields: allFields.filter(f => f.type === 'send-image').map(f => ({ key: f.key, type: f.type })),
      totalFields: allFields.length
    });
    
    allFields
      .filter((field) => field.type === 'send-image')
      .forEach((field) => {
        const subTab = this.getSubTabForField(field);
        
        // 🎯 PRIORIDADE 1: Data Binding com Observable
        if (subTab?.dataBinding?.getInitialData) {
          const initialData = subTab.dataBinding.getInitialData();
          if (initialData instanceof Observable) {
            console.log(`🔍 [DEBUG] Campo ${field.key} - usando dataBinding Observable`);
            
            this.imagePreviewsMap[field.key] = initialData.pipe(
              switchMap((imageIds: number[]) => {
                console.log(`🔍 [DEBUG] Campo ${field.key} - imageIds do dataBinding:`, imageIds);
                return this.getImagePreviewsWithBase64(imageIds);
              }),
              catchError((error) => {
                console.error(`❌ [DEBUG] Erro no dataBinding para ${field.key}:`, error);
                return of([]);
              }),
              tap((images) => {
                console.log(`✅ [DEBUG] Campo ${field.key} - imagens carregadas via dataBinding:`, images.length);
                this.initializeImageData(field.key, images);
              })
            );
            return;
          }
        }
        
        // 🎯 PRIORIDADE 2: Dados estáticos (existingImages já configurado)
        const existingIds = field.imageConfiguration?.existingImages || [];
        console.log(`🔍 [DEBUG] Campo ${field.key} - existingIds estáticos:`, existingIds);

        this.imagePreviewsMap[field.key] = this.getImagePreviewsWithBase64(
          existingIds
        ).pipe(
          catchError((error) => {
            console.error(`❌ [DEBUG] Erro ao carregar imagens para ${field.key}:`, error);
            return of([]);
          }),
          tap((images) => {
            console.log(`✅ [DEBUG] Campo ${field.key} - imagens carregadas:`, images.length);
            this.initializeImageData(field.key, images);
          })
        );
      });
  }

  /**
   * 🎯 ATIVAR MODO DE EDIÇÃO
   * 
   * Método chamado pelo botão "Editar" para ativar a edição do formulário
   */
  enableEditMode(): void {
    this.isEditMode = true;
    
    // 🎯 CORRIGIDO: Habilitar todos os campos de todas as sub-abas
    const allFields = this.getAllFieldsFromSubTabs();
    allFields.forEach(field => {
      if (field.disabled !== true && field.readOnly !== true) {
        const control = this.form.get(field.key);
        if (control) {
          control.enable();
        }
      }
    });
    
    // Reset do estado de salvamento
    this.isSavedSuccessfully = false;
    
    // Ativar change detection
    this.setupFormChangeDetection();

    // 🔧 CORREÇÃO: Verificar se tabItem existe antes de modificar
    if (this.tabItem) {
      this.tabItem.isModified = true;
    }
  }

  /**
   * 🎯 CANCELAR EDIÇÃO
   * 
   * Volta ao modo somente leitura e restaura valores originais
   */
  cancelEdit(): void {
    // 🎯 CORRIGIDO: Restaurar valores de todos os campos de todas as sub-abas
    const allFields = this.getAllFieldsFromSubTabs();
    allFields.forEach(field => {
      const originalValue = this.getInitialValue(field);
      const control = this.form.get(field.key);
      if (control) {
        control.setValue(originalValue);
        // 🔒 READONLY: Não desabilitar campos readOnly, eles permanecem habilitados mas não editáveis
        if (field.readOnly !== true) {
          control.disable();
        }
      }
    });
    
    this.isEditMode = false;
    this.isFormDirty = false;
    
    // Limpar change detection
    this.formSubscriptions.forEach(sub => sub.unsubscribe());
    this.formSubscriptions = [];

    // 🔧 CORREÇÃO: Verificar se tabItem existe antes de modificar
    if (this.tabItem) {
      this.tabItem.isModified = false;
    }
  }

  onSubmit() {
    console.log('🚨 [DEBUG] onSubmit chamado!', {
      formValid: this.form.valid,
      isSubmitting: this.isSubmitting,
      isEditMode: this.isEditMode,
      isNewItem: this.isNewItem,
      stackTrace: new Error().stack?.split('\n').slice(1, 6).join('\n')
    });
    
    // ✅ PROTEÇÃO ADICIONAL: Não executar submit se não estiver em modo de edição (exceto para novos itens)
    if (!this.isNewItem && !this.isEditMode) {
      console.log('🚫 [DEBUG] onSubmit BLOQUEADO - Não está em modo de edição');
      return;
    }
    
    if (this.form.valid && !this.isSubmitting) {
      console.log('🚨 [DEBUG] onSubmit EXECUTANDO - Formulário válido e não está submetendo');
      this.isSubmitting = true;
      
      // 🎯 NOVO: Processar campos computados ANTES de converter dados
      this.updateComputedFields();
      
      const rawFormData = { ...this.form.value };
      const allFields = this.getAllFieldsFromSubTabs();
      const imageUploadTasks: Observable<any>[] = [];
      const convertedData: { [key: string]: any } = {};
      
      // 🎯 CONVERSÃO DE TIPOS: Processar todos os campos baseado no tipo
      allFields.forEach(field => {
        const fieldValue = rawFormData[field.key];
        
        switch (field.type) {
          case 'number':
            convertedData[field.key] = fieldValue && fieldValue !== '' 
              ? Number(fieldValue) 
              : null;
            break;
          case 'kilometer-input':
            // 🎯 TRATAMENTO ESPECIAL: Kilometer input já converte internamente
            convertedData[field.key] = fieldValue && fieldValue !== '' 
              ? Number(fieldValue) 
              : null;
            break;
          case 'currency-input':
            // 🎯 TRATAMENTO ESPECIAL: Currency input já converte internamente
            convertedData[field.key] = fieldValue && fieldValue !== '' 
              ? Number(fieldValue) 
              : null;
            break;
          case 'date':
            convertedData[field.key] = fieldValue 
              ? new Date(fieldValue).toISOString() 
              : null;
            break;
          case 'select':
            // 🎯 TRATAMENTO ESPECIAL: Select com returnObjectSelected
            if (field.returnObjectSelected && fieldValue && typeof fieldValue === 'object') {
              // ✅ Objeto já está correto vindoDirect do ngValue
              convertedData[field.key] = fieldValue;
            } else if (field.returnObjectSelected && typeof fieldValue === 'string' && field.options) {
              // Se veio como string, procurar o objeto correspondente nas opções
              const foundOption = field.options.find(opt => 
                (typeof opt.value === 'object' && opt.value?.name?.toLowerCase() === fieldValue.toLowerCase()) ||
                opt.value === fieldValue
              );
              convertedData[field.key] = foundOption ? foundOption.value : fieldValue;
            } else {
              convertedData[field.key] = fieldValue;
            }
            break;
          case 'color-input':
            // 🎯 TRATAMENTO ESPECIAL: Color input sempre retorna objeto {name, code}
            convertedData[field.key] = fieldValue || null;
            break;
          case 'checkbox':
          case 'multi-select':
            convertedData[field.key] = fieldValue || [];
            break;
          case 'slide-toggle':
            convertedData[field.key] = Boolean(fieldValue);
            break;
          case 'send-image':
            // Será processado no bloco de upload abaixo
            break;
          default:
            convertedData[field.key] = fieldValue;
        }
      });
      
      // 🎯 CORREÇÃO: Processar campos de imagem com upload real
      allFields.forEach(field => {
        if (field.type === 'send-image') {
          const imageData = this.imageFieldData[field.key];
          if (imageData) {
            // 🚀 UPLOAD REAL: Usar método getImageIds para fazer upload
            const task = this.getImageIds(imageData).pipe(
              tap((ids) => {
                // 🎯 LÓGICA: maxImages = 1 → inteiro, > 1 → array
                const maxImages = field.imageConfiguration?.maxImages ?? 3;
                if (maxImages === 1) {
                  // Enviar como inteiro (ex: photoId: 187)
                  convertedData[field.key] = ids.length > 0 ? ids[0] : null;
                } else {
                  // Enviar como array (ex: photoIds: [187, 188])
                  convertedData[field.key] = ids;
                }
              })
            );
            imageUploadTasks.push(task);
          } else {
            // Campo sem dados de imagem
            const maxImages = field.imageConfiguration?.maxImages ?? 3;
            if (maxImages === 1) {
              convertedData[field.key] = null; // Inteiro nulo
            } else {
              convertedData[field.key] = []; // Array vazio
            }
          }
        }
      });

      // 🎯 AGUARDAR todos os uploads antes de emitir
      const finalTask = imageUploadTasks.length > 0 ? forkJoin(imageUploadTasks) : of([]);
      
      finalTask.subscribe({
        next: () => {
          this.formSubmit.emit(convertedData);
          // 🔧 CORREÇÃO: Verificar se tabItem existe antes de modificar
          if (this.tabItem) {
            this.tabItem.isModified = true;
          }
        },
        error: (error) => {
          console.error('Erro no upload de imagens:', error);
          this.isSubmitting = false;
          // Ainda assim emitir dados para não travar o formulário
          this.formSubmit.emit(convertedData);
        }
      });
      
    } else {
      this.markAllFieldsAsTouched();
    }
  }

  onCancel() {
    this.formCancel.emit();
  }

  private markAllFieldsAsTouched() {
    Object.keys(this.form.controls).forEach(key => {
      this.form.get(key)?.markAsTouched();
    });
  }

  getFormControl(key: string): FormControl {
    return this.form.get(key) as FormControl;
  }

  getInitialValue(field: TabFormField): any {
    if (this.initialData && this.initialData[field.key] !== undefined) {
      const value = this.initialData[field.key];
      
      // 🎯 TRATAMENTO ESPECIAL: multi-select com array de strings da API
      if (field.type === 'multi-select' && field.key === 'driver_license_category') {
        return this.convertApiArrayToMultiSelectFormat(value);
      }
      
      // 🎯 TRATAMENTO ESPECIAL: Select com returnObjectSelected
      if (field.type === 'select' && field.returnObjectSelected && field.options) {
        // Se o valor da API é um objeto, usar diretamente
        if (typeof value === 'object' && value !== null) {
          return value;
        }
        
        // Se o valor da API é string, procurar o objeto correspondente
        if (typeof value === 'string' && value) {
          const foundOption = field.options.find(opt => {
            if (typeof opt.value === 'object') {
              // Usar o método genérico de comparação string com objeto
              return this.compareStringWithObject(value, opt.value, opt.label);
            }
            return opt.value === value;
          });
          
          if (foundOption) {
            return foundOption.value;
          }
        }
        
        // Se não encontrou correspondência, retornar valor original
        return value;
      }
      
      return value;
    }
    
    // 🎯 TRATAMENTO ESPECIAL: slide-toggle deve ter valor boolean padrão
    if (field.type === 'slide-toggle') {
      return field.defaultValue !== undefined ? field.defaultValue : false;
    }
    
    return field.defaultValue || '';
  }

  getCheckboxControl(key: string): FormControl {
    return this.form.get(key) as FormControl;
  }

  /**
   * 🎯 CONVERSÃO: Array de strings da API para formato multi-select
   * Converte ['A', 'B'] para ['A', 'B'] (direto, já que multi-select usa array de strings)
   */
  private convertApiArrayToMultiSelectFormat(apiValue: string | string[]): string[] {
    // Normaliza o valor da API para array
    if (Array.isArray(apiValue)) {
      return apiValue;
    } else if (typeof apiValue === 'string' && apiValue) {
      return [apiValue];
    }
    
    return [];
  }

  // Métodos para manipulação de imagens
  handleImageChange(fieldKey: string, files: File[]) {
    if (!this.imageFieldData[fieldKey]) {
      this.imageFieldData[fieldKey] = { images: [], files: [] };
    }
    this.imageFieldData[fieldKey].files = files;
  }

  initializeImageData(fieldKey: string, initialImages: ImagesIncludeId[]) {
    console.log(`🔍 [DEBUG] initializeImageData chamado para ${fieldKey}:`, {
      initialImages,
      length: initialImages.length,
      hasImages: initialImages.length > 0
    });
    
    this.imageFieldData[fieldKey] = {
      images: initialImages,
      files: []
    };
    
    console.log(`✅ [DEBUG] imageFieldData[${fieldKey}] configurado:`, this.imageFieldData[fieldKey]);
  }

  handlePreviewImagesChange(fieldKey: string, images: ImagesIncludeId[]) {
    console.log(`🔍 [DEBUG] handlePreviewImagesChange chamado para ${fieldKey}:`, {
      images,
      length: images.length,
      hasImages: images.length > 0
    });
    
    if (!this.imageFieldData[fieldKey]) {
      this.imageFieldData[fieldKey] = { images: [], files: [] };
    }
    this.imageFieldData[fieldKey].images = images;

    // 🎯 Atualizar imagem do side-card quando imagens mudam
    this.updateSideCardImage();
  }

  handleImageError(msg: string) {
    // this.logger.error('Erro no upload de imagem:', msg);
  }

  // ========================================
  // 📄 MÉTODOS PDF UPLOADER
  // ========================================

  handlePdfAttachmentChange(fieldKey: string, attachments: any[]) {
    if (!this.pdfFieldData[fieldKey]) {
      this.pdfFieldData[fieldKey] = { attachments: [], files: [] };
    }
    this.pdfFieldData[fieldKey].attachments = attachments;
    
    // Atualizar o valor do formulário
    const control = this.form.get(fieldKey);
    if (control) {
      control.setValue(attachments);
      control.markAsDirty();
    }
    
    console.log(`📎 PDF attachments atualizados para ${fieldKey}:`, attachments);
  }

  handlePdfFilesChange(fieldKey: string, files: File[]) {
    if (!this.pdfFieldData[fieldKey]) {
      this.pdfFieldData[fieldKey] = { attachments: [], files: [] };
    }
    this.pdfFieldData[fieldKey].files = files;
    
    console.log(`📁 PDF files selecionados para ${fieldKey}:`, files);
  }


  handlePdfError(error: string) {
    console.error('❌ Erro no upload de PDF:', error);
    // TODO: Integrar com sistema de notificações
  }

  handlePdfDocumentUploaded(fieldKey: string, document: any) {
    console.log(`✅ PDF documento enviado para ${fieldKey}:`, document);
  }

  initializePdfData(fieldKey: string, initialAttachments: any[]) {
    console.log(`📄 Inicializando dados PDF para ${fieldKey}:`, initialAttachments);
    
    if (!this.pdfFieldData[fieldKey]) {
      this.pdfFieldData[fieldKey] = { attachments: [], files: [] };
    }
    
    this.pdfFieldData[fieldKey].attachments = initialAttachments;
    
    // Atualizar o observable
    this.pdfAttachmentsMap[fieldKey] = of(initialAttachments);
  }

  /**
   * 🚀 MÉTODO PRINCIPAL: Processa upload de imagens e retorna IDs
   */
  getImageIds(
    imageData: { images?: ImagesIncludeIdExtended[]; files?: File[] } = {}
  ): Observable<number[]> {
    if (!imageData || (!imageData.images && !imageData.files)) {
      return of([]);
    }
    
    const safeData = {
      images: imageData.images?.filter(
        (img) => img !== null && img.id !== undefined
      ) || [],
      files: imageData.files || [],
    };

    // IDs de imagens já existentes
    const existingIds = safeData.images
      .filter(
        (img): img is { id: number; image: any } =>
          typeof img.id === "number" && !isNaN(img.id)
      )
      .map((img) => img.id);

    // Arquivos para upload (novos + imagens base64 sem ID)
    const filesToUpload = [
      ...safeData.files,
      ...safeData.images
        .filter((img) => !img.id && img.image && !img.file)
        .map((img) => this.convertToFile(img.image)),
    ].filter(Boolean) as File[];

    if (filesToUpload.length === 0) {
      return of(existingIds);
    }

    // Upload de novos arquivos
    const uploadObservables = filesToUpload.map((file) =>
      this.uploadFile(file).pipe(
        map((id) => Number(id)),
        catchError((error) => {
          console.error("Falha no upload:", error);
          return of(null);
        }),
        shareReplay(1)
      )
    );

    return forkJoin(uploadObservables).pipe(
      map((newIds) => {
        const validNewIds = newIds.filter((id): id is number => id !== null);
        return [...existingIds, ...validNewIds];
      })
    );
  }

  /**
   * 🚀 UPLOAD DE ARQUIVO: Faz upload para S3 e confirma
   */
  private uploadFile(file: File): Observable<string> {
    const uploadFile =
      file.name === "uploaded-image"
        ? new File(
            [file],
            `image-${Date.now()}${this.getFileExtension(file.type)}`,
            { type: file.type }
          )
        : file;

    return this.imageUploadService.uploadImageUrl(uploadFile.name).pipe(
      switchMap((dataUrlFile: DataUrlFile) =>
        this.http
          .put(dataUrlFile.data.uploadUrl, uploadFile)
          .pipe(
            switchMap(() =>
              this.imageUploadService.uploadImageConfirm(
                dataUrlFile.data.storageKey
              )
            )
          )
      ),
      map((response: any) => response.data.id.toString()),
      take(1)
    );
  }


  /**
   * 🔄 CONVERSÃO: Base64 ou File para File
   */
  private convertToFile(image: string | File): File | null {
    if (image instanceof File) {
      return image;
    }

    try {
      const mimeType = image.match(/data:(.*?);/)?.[1] || "image/jpeg";
      const fileName = this.extractFileNameFromBase64(image) || "custom-name";

      const byteString = atob(image.split(",")[1]);
      const ab = new ArrayBuffer(byteString.length);
      const ia = new Uint8Array(ab);

      for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }

      return new File([new Blob([ab], { type: mimeType })], fileName, {
        type: mimeType,
      });
    } catch (error) {
      console.error("Conversão falhou:", error);
      return null;
    }
  }

  /**
   * 🔧 UTILITÁRIO: Extrai nome do arquivo de base64
   */
  private extractFileNameFromBase64(base64: string): string | null {
    const metadataMatch = base64.match(/filename=(.*?);/);
    if (metadataMatch && metadataMatch[1]) {
      return metadataMatch[1];
    }
    return null;
  }

  /**
   * 🔧 UTILITÁRIO: Extensão do arquivo por MIME type
   */
  private getFileExtension(mimeType: string): string {
    const extensions: { [key: string]: string } = {
      "image/jpeg": ".jpg",
      "image/png": ".png",
      "image/gif": ".gif",
      "image/webp": ".webp",
    };
    return extensions[mimeType] || "";
  }

  /**
   * 🎯 BUSCA DE IMAGENS: Carrega imagens existentes por ID
   */
  private getImagePreviewsWithBase64(imageIds: number[]): Observable<ImagesIncludeId[]> {
    console.log('🔍 [DEBUG] getImagePreviewsWithBase64 chamado com:', imageIds);
    
    if (!imageIds || imageIds.length === 0) {
      console.log('🔍 [DEBUG] getImagePreviewsWithBase64 - sem IDs, retornando array vazio');
      return of([]);
    }

    // 🚀 IMPLEMENTAÇÃO REAL: Buscar imagens por ID
    console.log(`🔍 [DEBUG] getImagePreviewsWithBase64 - buscando ${imageIds.length} imagens`);
    return forkJoin(imageIds.map((id) => this.loadSingleImage(id)));
  }

  /**
   * 🔍 CARREGA IMAGEM: Busca uma imagem específica por ID
   */
  private loadSingleImage(id: number): Observable<ImagesIncludeId> {
    console.log(`🔍 [DEBUG] loadSingleImage chamado para ID: ${id}`);
    
    return this.imageUploadService.getImageById(id).pipe(
      tap((response) => {
        console.log(`✅ [DEBUG] loadSingleImage ${id} - resposta da API:`, response);
      }),
      switchMap((response) =>
        this.convertToImageIncludeId(id, response.data.downloadUrl)
      ),
      tap((result) => {
        console.log(`✅ [DEBUG] loadSingleImage ${id} - conversão concluída:`, result);
      }),
      catchError((error) => {
        console.error(`❌ [DEBUG] Error loading image ${id}:`, error);
        return of(this.createDefaultImage(id));
      })
    );
  }

  /**
   * 🔄 CONVERSÃO: URL para Base64
   */
  private convertToImageIncludeId(
    id: number,
    url: string
  ): Observable<ImagesIncludeId> {
    return this.convertImageUrlToBase64(url).pipe(
      map((base64) => ({ id, image: base64 }))
    );
  }

  /**
   * 🔄 CONVERSÃO: URL de imagem para Base64
   */
  private convertImageUrlToBase64(imageUrl: string): Observable<string> {
    console.log(`🔍 [DEBUG] convertImageUrlToBase64 chamado com URL:`, imageUrl);
    
    return new Observable((observer) => {
      const img = new Image();
      img.crossOrigin = "Anonymous";
      img.src = imageUrl;

      img.onload = () => {
        console.log(`✅ [DEBUG] convertImageUrlToBase64 - imagem carregada:`, {
          width: img.width,
          height: img.height,
          src: img.src
        });
        
        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext("2d");
        canvas.width = img.width;
        canvas.height = img.height;
        ctx?.drawImage(img, 0, 0);

        try {
          const dataUrl = canvas.toDataURL("image/png");
          console.log(`✅ [DEBUG] convertImageUrlToBase64 - conversão concluída, tamanho:`, dataUrl.length);
          observer.next(dataUrl);
          observer.complete();
        } catch (e) {
          console.error(`❌ [DEBUG] convertImageUrlToBase64 - erro na conversão:`, e);
          observer.error(e);
        }
      };

      img.onerror = (err) => {
        console.error(`❌ [DEBUG] convertImageUrlToBase64 - erro ao carregar imagem:`, err);
        observer.error(err);
      };
    });
  }

  /**
   * 🔧 FALLBACK: Imagem padrão para erro
   */
  private createDefaultImage(id: number): ImagesIncludeId {
    return {
      id,
      image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2NjYyIvPgogIDx0ZXh0IHg9IjUwIiB5PSI1NSIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjE0IiBmaWxsPSIjNjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5FcnJvcjwvdGV4dD4KPC9zdmc+'
    };
  }

  // Métodos utilitários públicos
  resetForm() {
    console.log(`🔍 [GenericTabForm-DEBUG] resetForm chamado:`, {
      stackTrace: new Error().stack?.split('\n').slice(1, 4).join('\n')
    });
    this.form.reset();
    this.isFormDirty = false;
    this.isSubmitting = false;
    this.isSavedSuccessfully = false;
  }

  updateFormData(data: any) {
    console.log(`🔍 [GenericTabForm-DEBUG] updateFormData chamado:`, {
      data,
      isEditMode: this.isEditMode,
      isFormDirty: this.isFormDirty,
      willUpdate: !(this.isEditMode && this.isFormDirty),
      stackTrace: new Error().stack?.split('\n').slice(1, 4).join('\n')
    });
    
    // 🎯 PROTEÇÃO: Só atualizar se não estiver em modo de edição ativa
    if (this.isEditMode && this.isFormDirty) {
      console.log(`🛡️ [GenericTabForm-DEBUG] Bloqueado updateFormData - em edição ativa`);
      return;
    }

    this.form.patchValue(data);
    
    // Atualizar também o initialData
    if (this.initialData) {
      this.initialData = { ...this.initialData, ...data };
    }
    
    // 🎯 Atualizar imagem do side-card quando dados mudam
    this.updateSideCardImage();
    
    // Forçar detecção de mudanças
    this.form.markAsPristine();
    this.form.markAsUntouched();
  }

  setSubmitting(submitting: boolean) {
    this.isSubmitting = submitting;
  }

  /**
   * Marca o formulário como salvo com sucesso
   * Isso impede que a crítica de "dados não salvos" seja exibida
   */
  markAsSavedSuccessfully() {
    console.log('🎯 markAsSavedSuccessfully chamado:', {
      isNewItem: this.isNewItem,
      isEditMode: this.isEditMode,
      isSubmitting: this.isSubmitting,
      isFormDirty: this.isFormDirty,
      isSavedSuccessfully: this.isSavedSuccessfully
    });
    
    this.isSavedSuccessfully = true;
    this.isFormDirty = false;
    this.isSubmitting = false;
    
    // Após salvar: Voltar ao modo somente leitura
    if (!this.isNewItem) {
      console.log('🔄 Item existente - voltando ao modo somente leitura');
      this.isEditMode = false;
      
      // 🎯 CORRIGIDO: Desabilitar todos os campos de todas as sub-abas
      const allFields = this.getAllFieldsFromSubTabs();
      allFields.forEach(field => {
        if (field.disabled !== true && field.readOnly !== true) {
          const control = this.form.get(field.key);
          if (control) {
            control.disable();
            console.log(`🔒 Campo ${field.key} desabilitado`);
          }
        }
      });
      
      // Limpar change detection para evitar interferências
      this.formSubscriptions.forEach(sub => sub.unsubscribe());
      this.formSubscriptions = [];
      console.log('🧹 Change detection limpo');
    } else {
      console.log('📝 Item novo - mantendo modo de edição ativo');
    }
    
    console.log('✅ Estado final após markAsSavedSuccessfully:', {
      isEditMode: this.isEditMode,
      isSubmitting: this.isSubmitting,
      isSavedSuccessfully: this.isSavedSuccessfully,
      isFormDirty: this.isFormDirty,
      fieldsDisabled: this.getAllFieldsFromSubTabs().map(f => ({
        key: f.key,
        disabled: this.form.get(f.key)?.disabled
      }))
    });
    
    // 🚨 TESTE: Desabilitar setTimeout que pode estar causando interferência tardia
    console.log(`🔍 [TEST] setTimeout em markAsSavedSuccessfully DESABILITADO`);
    
    // REMOVIDO TEMPORARIAMENTE:
    // setTimeout(() => {
    //   console.log('🔄 Aplicando mudanças na UI após salvamento...');
    //   console.log('✅ Mudanças na UI aplicadas sem interferir em componentes filhos');
    // }, 100);
  }

  /**
   * Verifica se o formulário pode ser fechado sem perda de dados
   * @returns true se pode fechar, false se há dados não salvos
   */
  canCloseWithoutWarning(): boolean {
    return !this.isFormDirty || this.isSavedSuccessfully;
  }

  /**
   * Verifica se há dados não salvos que precisam de confirmação
   * @returns true se há dados não salvos, false caso contrário
   */
  hasUnsavedChanges(): boolean {
    return this.isFormDirty && !this.isSavedSuccessfully;
  }

  /**
   * Reseta o estado de salvamento (útil ao fazer novas alterações após salvar)
   */
  resetSaveState() {
    this.isSavedSuccessfully = false;
  }

  /**
   * 🎯 MARCA COMO DIRTY SEM EVENTOS EXTERNOS
   * 
   * Usado durante edição ativa para evitar loops de ngOnChanges
   */
  markAsDirty(): void {
    this.isFormDirty = true;
    this.isSavedSuccessfully = false;
  }

  /**
   * 🎯 NOVO: Getter para título resolvido com templates
   * 
   * Resolve placeholders {{campo}} com dados do item atual
   */
  get resolvedTitle(): string {
    if (!this.config.title) {
      return 'Formulário';
    }
    
    // 🎯 Se contém placeholders, resolver com dados
    if (this.config.title.includes('{{')) {
      const data = this.tabItem?.data || this.initialData || {};
      return this.resolveTitleTemplate(this.config.title, data);
    }
    
    // 🎯 Título estático
    return this.config.title;
  }

  /**
   * 🎯 NOVO: Resolve template de título com dados
   * 
   * Substitui {{campo}} pelos valores correspondentes
   */
  private resolveTitleTemplate(template: string, data: any): string {
    const resolved = template.replace(/\{\{(.+?)\}\}/g, (match, propName) => {
      const cleanPropName = propName.trim();
      const value = data[cleanPropName];
      
      // 🎯 Se valor existe e não está vazio, usar
      if (value !== null && value !== undefined && value !== '') {
        return value.toString();
      }
      
      // 🎯 Placeholder vazio para ser tratado depois
      return '';
    });
    
    // 🎯 Verificar se todos os placeholders ficaram vazios
    const hasEmptyPlaceholders = resolved.includes(': ') && resolved.endsWith(': ') ||
                                resolved.includes(': -') ||
                                resolved.trim() === '' ||
                                resolved.includes(': ()');
    
    if (hasEmptyPlaceholders) {
      // 🎯 Usar titleFallback se definido
      if (this.config.titleFallback) {
        return this.config.titleFallback;
      }
      
      // 🎯 Fallback automático baseado no estado
      if (this.isNewItem) {
        return `Novo ${this.config.entityType || 'Item'}`;
      }
      
      return this.config.entityType || 'Item';
    }
    
    return resolved;
  }

  loadApiOptions(field: TabFormField) {
    // Verificar se já carregou as opções para este campo
    if (this.apiOptionsMap[field.key] && this.apiOptionsMap[field.key].length > 0) {
      return;
    }

    // Verificar se o campo tem função de API
    if (field.fetchApi && typeof field.fetchApi === 'function') {
      field.fetchApi('').subscribe({
        next: (options) => {
          this.apiOptionsMap[field.key] = options;
        },
        error: (error) => {
          this.apiOptionsMap[field.key] = [];
        }
      });
    } else {
      // Fallback: definir array vazio se não há API
      this.apiOptionsMap[field.key] = [];
    }
  }

  /**
   * 🎯 MANIPULAÇÃO PROTEGIDA DE CEP
   * 
   * Busca dados do endereço via CEP quando em modo de edição
   */
  handleCepChange(cep: string): void {
    // 🎯 PROTEÇÃO: Só processar CEP se estiver em modo de edição
    if (!this.isEditMode) {
      return;
    }

    const cleanCep = cep?.replace(/\D/g, '') || '';
    
    if (cleanCep.length === 8) {
      this.cepService.search(cleanCep).subscribe({
        next: (data: any) => {
          if (!data.erro) {
            // Aplicar dados do CEP
            this.form.patchValue({
              address_street: data.logradouro || '',
              address_neighborhood: data.bairro || '',
              address_city: data.localidade || '',
              address_uf: data.uf || '',

            });
          }
        },
        error: (error) => {
          // Erro silencioso - CEP pode não existir
        }
      });
    }
  }

  selectSubTab(tab: string) {
    console.log(`🎯 [GenericTabForm] Selecionando sub-aba: ${tab}`);
    console.log(`🎯 [GenericTabForm] Sub-aba anterior: ${this.selectedSubTab}`);
    
    // 🔥 DEBUG ESPECÍFICO PARA ABA PARADAS
    if (tab === 'paradas') {
      console.log(`🔥 [DEBUG PARADAS] ABA PARADAS SELECIONADA!`);
      console.log(`🔥 [DEBUG PARADAS] Config disponível:`, this.config);
      console.log(`🔥 [DEBUG PARADAS] subTabs:`, this.config?.subTabs);
    }
    
    // Destruir componente da aba anterior se existir
    if (this.selectedSubTab && this.selectedSubTab !== tab) {
      this.destroyDynamicComponent(this.selectedSubTab);
    }

    this.selectedSubTab = tab;
    console.log(`✅ [GenericTabForm] Sub-aba selecionada: ${this.selectedSubTab}`);

    // 🔧 REATIVADO: SubTab navigation simplificada sem Zone.js
    setTimeout(() => {
      const subTabConfig = this.getSubTabConfig(tab);
      if (subTabConfig && this.hasDynamicComponent(subTabConfig)) {
        console.log(`🚀 [GenericTabForm] Criando componente dinâmico simplificado: ${tab}`);
        this.createDynamicComponentForSubTab(subTabConfig);
      } else {
        console.log(`⚠️ [GenericTabForm] Sub-aba ${tab} não tem componente dinâmico`);
      }
    }, 50);
    
    // Resetar estado se necessário
    this.resetSaveState();
    
    // 🚨 REMOVIDO: cdr.detectChanges() que pode interferir no remote-select
  }

  getAddressData(): AddressData {
    return {
      cep: this.form.get('address_cep')?.value || '',
      street: this.form.get('address_street')?.value || '',
      neighborhood: this.form.get('address_neighborhood')?.value || '',
      city: this.form.get('address_city')?.value || '',
      state: this.form.get('address_uf')?.value || '',
      number: this.form.get('address_number')?.value || '',
      complement: this.form.get('address_complement')?.value || ''
    };
  }
  getPhotosData(): any {
    return {
      photoIds: this.form.get('photoIds')?.value || []  
    };
  }

  onAddressDataChange(data: AddressData) {
    // 🎯 PROTEÇÃO: Só atualizar se estiver em modo de edição
    if (!this.isEditMode) {
      return;
    }

    // Atualizar os campos do formulário principal
    this.form.patchValue({
      address_cep: data.cep,
      address_street: data.street,
      address_neighborhood: data.neighborhood,
      address_city: data.city,
      address_uf: data.state,
      address_number: data.number,
      address_complement: data.complement
    });
  }

  onAddressCepSearched(cep: string) {
    this.handleCepChange(cep);
  }

  private initializeDefaultSubTab() {
    const availableSubTabs = this.getAvailableSubTabs();
    if (availableSubTabs.length > 0) {
      this.selectedSubTab = availableSubTabs[0].id;
    } else {
      // Fallback para aba dados se não há configuração
      this.selectedSubTab = 'dados';
    }
  }

  /**
   * Retorna as sub-abas disponíveis baseado na configuração
   */
  getAvailableSubTabs(): SubTabConfig[] {
    if (!this.config.subTabs || this.config.subTabs.length === 0) {
      // Configuração padrão se não especificada
      return [
        {
          id: 'dados',
          label: 'Dados Básicos',
          icon: 'fa-user',
          enabled: true,
          order: 1
        }
      ];
    }

    return this.config.subTabs
      .filter(tab => tab.enabled !== false) // Remove abas explicitamente desabilitadas
      .sort((a, b) => (a.order || 999) - (b.order || 999)); // Ordena por order
  }

  /**
   * Verifica se uma sub-aba específica está disponível
   */
  isSubTabAvailable(tabId: string): boolean {
    return this.getAvailableSubTabs().some(tab => tab.id === tabId);
  }

  /**
   * Retorna configuração de uma sub-aba específica
   */
  getSubTabConfig(tabId: string): SubTabConfig | undefined {
    return this.config.subTabs?.find(tab => tab.id === tabId);
  }

  // ✨ Métodos para Card Lateral

  /**
   * 🎯 NOVO MÉTODO: Buscar imagem das que já foram carregadas
   * Verifica se existem imagens carregadas para o campo especificado
   */
  // 🎯 IMAGEM REATIVA: Evita execução constante no template
  public sideCardImageUrl: string | null = null;

  /**
   * 🎯 ATUALIZAR: Imagem do side-card de forma reativa
   */
  private updateSideCardImage(): void {
    this.sideCardImageUrl = this.calculateSideCardImageUrl();
  }

  /**
   * 🎯 CALCULAR: URL da imagem do side-card (executado apenas quando necessário)
   */
  private calculateSideCardImageUrl(): string | null {
    const sideCard = (this.config as any).sideCard;
    if (!sideCard?.data?.imageField) {
      return this.getDefaultImageByDomain();
    }

    const imageField = sideCard.data.imageField;
    
    // 🚀 PRIORIDADE 1: Imagens já carregadas no componente
    const loadedImage = this.getSideCardImageFromPreview(imageField);
    if (loadedImage) {
      return loadedImage;
    }
    
    // 🔄 PRIORIDADE 2: Dados do formulário
    const imageValue = this.getFieldValue(imageField);
    
    // Se for uma URL direta e válida
    if (typeof imageValue === 'string' && imageValue.startsWith('http')) {
      return imageValue;
    }

    // Se for um array de imagens, pega a primeira
    if (Array.isArray(imageValue) && imageValue.length > 0) {
      const firstImage = imageValue[0];
      if (typeof firstImage === 'string' && firstImage.startsWith('http')) {
        return firstImage;
      }
      if (firstImage && firstImage.url && firstImage.url.startsWith('http')) {
        return firstImage.url;
      }
    }

    // ✨ Fallback: Imagem padrão por domínio
    return this.getDefaultImageByDomain();
  }

  private getSideCardImageFromPreview(imageField: string): string | null {
    // 🎯 VERIFICAR: Imagens carregadas em imageFieldData
    const fieldData = this.imageFieldData[imageField];
    
    if (fieldData?.images && fieldData.images.length > 0) {
      const firstImage = fieldData.images[0];
      if (firstImage.image && typeof firstImage.image === 'string' && firstImage.image.startsWith('data:')) {
        return firstImage.image; // Base64 válido
      }
    }

    return null;
  }

  getSideCardImageUrl(): string | null {
    // 1️⃣ Se tem imagem real do objeto, usa ela
    if (this.sideCardImageUrl) {
      return this.sideCardImageUrl;
    }
    
    // 2️⃣ Verifica se deve mostrar imagem padrão
    const sideCardConfig = (this.config as any)?.sideCard;
    const showDefaultImage = sideCardConfig?.data?.showDefaultImageWhenEmpty !== false; // Default: true
    
    if (!showDefaultImage) {
      return null; // Não exibe imagem se configurado para não mostrar
    }
    
    // 3️⃣ Busca imagem padrão por domínio
    // Tenta várias fontes para detectar o domínio
    const domain = this.config?.entityType || 
                   this.tabItem?.type || 
                   (this.config as any)?.domain || 
                   'default';
    
    const defaultImage = this.DOMAIN_DEFAULT_IMAGES[domain];
    
    if (defaultImage) {
      return defaultImage;
    }
    
    // 4️⃣ Fallback: sem imagem configurada = não exibe
    return null;
  }

  getFieldValue(fieldKey: string): any {
    // Primeiro verifica nos dados do formulário atual
    const formValue = this.form.get(fieldKey)?.value;
    if (formValue !== null && formValue !== undefined && formValue !== '') {
      return formValue;
    }

    // Depois verifica nos dados iniciais/tabItem
    if (this.tabItem && (this.tabItem as any)[fieldKey] !== null && (this.tabItem as any)[fieldKey] !== undefined) {
      return (this.tabItem as any)[fieldKey];
    }

    // Por último, nos dados iniciais
    if (this.initialData && this.initialData[fieldKey] !== null && this.initialData[fieldKey] !== undefined) {
      return this.initialData[fieldKey];
    }

    // 🎯 NOVO: Buscar defaultValue na configuração do campo
    const fieldConfig = this.getFieldConfig(fieldKey);
    if (fieldConfig?.defaultValue !== null && fieldConfig?.defaultValue !== undefined) {
      return fieldConfig.defaultValue;
    }

    return '';
  }

  /**
   * 🔍 Busca configuração de um campo específico em todas as sub-abas
   */
  private getFieldConfig(fieldKey: string): any {
    if (!this.config?.subTabs) return null;
    
    for (const subTab of this.config.subTabs) {
      if (subTab.fields) {
        const field = subTab.fields.find(f => f.key === fieldKey);
        if (field) return field;
      }
    }
    
    // Buscar também nos campos globais
    if (this.config.fields) {
      const field = this.config.fields.find(f => f.key === fieldKey);
      if (field) return field;
    }
    
    return null;
  }

  getStatusConfig(status: any): any {
    const sideCard = (this.config as any).sideCard;
    if (!sideCard?.data?.statusConfig) {
      return null;
    }
    
    // ✅ Permitir status vazio para usar fallback
    if (status === null || status === undefined) {
      status = '';
    }
    
    const statusStr = status.toString().toLowerCase();
    const statusConfig = sideCard.data.statusConfig;
    
    // Procurar primeiro pelo valor exato
    if (statusConfig[statusStr]) {
      return statusConfig[statusStr];
    }
    
    // Procurar pelo valor original (sem toLowerCase)
    if (statusConfig[status.toString()]) {
      return statusConfig[status.toString()];
    }
    
    // Procurar por variações comuns
    const statusVariations = [statusStr, status.toString()];
    for (const variation of statusVariations) {
      if (statusConfig[variation]) {
        return statusConfig[variation];
      }
    }
    
    // Fallback para status não mapeado
    if (statusConfig['*']) {
      return statusConfig['*'];
    }
    
    // Último fallback - status padrão
    return {
      label: status.toString(),
      color: "#f5f5f5",
      textColor: "#666",
      icon: "fa-question-circle"
    };
  }

  formatCurrency(value: any): string {
    if (value === null || value === undefined || value === '') {
      return 'R$ 0,00';
    }

    const numValue = typeof value === 'string' ? parseFloat(value) : value;
    if (isNaN(numValue)) {
      return 'R$ 0,00';
    }

    return new Intl.NumberFormat('pt-BR', {
      style: 'currency',
      currency: 'BRL'
    }).format(numValue);
  }

  formatFieldValue(value: any, field: any): string {
    if (value === null || value === undefined || value === '') {
      // ⚠️ ALERTA: Valor vazio para categorias da CNH
      if (field.key === 'driver_license_category') {
        return '⚠️ CNH OBRIGATÓRIA';
      }
      return '-';
    }

    // ✨ NOVO: Suporte para formatação customizada com funções
    if (field.format && typeof field.format === 'function') {
      return field.format(value);
    }

    // Formatação específica baseada no tipo ou formato (string)
    if (field.format && typeof field.format === 'string') {
      switch (field.format) {
        case 'date':
          return new Date(value).toLocaleDateString('pt-BR');
        case 'datetime':
          return new Date(value).toLocaleString('pt-BR');
        case 'distance':
          // ✨ NOVO: Formatação de distância
          const distance = parseFloat(value);
          if (isNaN(distance)) return '-';
          if (distance >= 1000) {
            return `${(distance / 1000).toFixed(1).replace('.', ',')} mil km`;
          }
          return `${distance.toFixed(0)} km`;
        case 'duration':
          // ✨ NOVO: Formatação de duração (minutos para horas e minutos)
          const minutes = parseInt(value);
          if (isNaN(minutes)) return '-';
          if (minutes < 60) {
            return `${minutes} min`;
          }
          const hours = Math.floor(minutes / 60);
          const remainingMinutes = minutes % 60;
          if (remainingMinutes === 0) {
            return `${hours}h`;
          }
          return `${hours}h ${remainingMinutes}min`;
        case 'currency':
          // ✨ MELHORADO: Formatação de moeda brasileira
          const amount = parseFloat(value);
          if (isNaN(amount)) return '-';
          return new Intl.NumberFormat('pt-BR', {
            style: 'currency',
            currency: 'BRL',
            minimumFractionDigits: 2,
            maximumFractionDigits: 2
          }).format(amount);
        case 'uppercase':
          return value.toString().toUpperCase();
        case 'capitalize':
          return value.toString().charAt(0).toUpperCase() + value.toString().slice(1).toLowerCase();
        case 'array-all-uppercase':
          // ✅ NOVO: Suporte para arrays com todos os itens em uppercase
          if (Array.isArray(value)) {
            // ⚠️ ALERTA: Array vazio ou com strings vazias para categorias da CNH
            if (field.key === 'driver_license_category') {
              const validCategories = value.filter(item => 
                item && typeof item === 'string' && item.toString().trim() !== ''
              );
              if (validCategories.length === 0) {
                return '⚠️ CNH OBRIGATÓRIA';
              }
              return validCategories.map(item => item.toString().toUpperCase()).join(', ');
            }
            return value.map(item => item.toString().toUpperCase()).join(', ');
          }
          // ⚠️ ALERTA: String vazia para categorias da CNH
          if (field.key === 'driver_license_category' && (!value || value.toString().trim() === '')) {
            return '⚠️ CNH OBRIGATÓRIA';
          }
          return value.toString().toUpperCase();
        case 'gender':
          // ✅ NOVO: Suporte para formatação de gênero
          const genderLabels: { [key: string]: string } = {
            'male': 'Masculino',
            'female': 'Feminino', 
            'other': 'Outro'
          };
          return genderLabels[value?.toLowerCase()] || value || '-';
        default:
          return value.toString();
      }
    }

    return value.toString();
  }

  // private getVehicleDefaultImage(): string {
  //   // Imagem padrão para veículos baseada nos dados
  //   const brand = this.getFieldValue('brand') || this.getFieldValue('marca');
  //   const model = this.getFieldValue('model') || this.getFieldValue('modelo');
    
  //   // Aqui você pode implementar lógica para retornar imagens específicas por marca
  //   // Por enquanto, retorna uma imagem genérica de carro
  //   return 'https://via.placeholder.com/300x200/f0f0f0/666666?text=' + 
  //          encodeURIComponent((brand && model) ? `${brand} ${model}` : 'Veículo');
  // }

  // ✨ Imagem padrão para o side card (DEPRECATED - usar getDefaultImageByDomain)
  private getDefaultSideCardImage(): string {
    return 'assets/imagens/7e9291dd-d62e-4879-ad92-4d47b94d4fee.png';
  }

  // 🎨 NOVO: Imagem padrão baseada no domínio
  private getDefaultImageByDomain(): string | null {
    // Verifica se deve mostrar imagem padrão
    const sideCardConfig = (this.config as any)?.sideCard;
    const showDefaultImage = sideCardConfig?.data?.showDefaultImageWhenEmpty !== false; // Default: true
    
    if (!showDefaultImage) {
      return null; // Não exibe imagem se configurado para não mostrar
    }
    
    // Detecta domínio
    const domain = this.config?.entityType || 
                   this.tabItem?.type || 
                   (this.config as any)?.domain || 
                   'default';
    
    // Busca imagem padrão do domínio
    const defaultImage = this.DOMAIN_DEFAULT_IMAGES[domain];
    
    return defaultImage || null;
  }

  // ✨ Tratamento de erro de carregamento de imagem
  onImageError(event: any): void {
    const imgElement = event.target as HTMLImageElement;
    const defaultImage = this.getDefaultImageByDomain();
    
    // Evita loop infinito se a imagem padrão também falhar
    if (imgElement.src !== defaultImage) {
      imgElement.src = defaultImage || '';
    } else {
      // Se até a imagem padrão falhar, esconde a div da imagem
      imgElement.style.display = 'none';
    }
  }

  // ✨ Getters para template

  get sideCardEnabled(): boolean {
    const sideCardConfig = (this.config as any).sideCard;
    
    // Se não há configuração de sideCard, não mostrar
    if (!sideCardConfig?.enabled) {
      return false;
    }
    
    // 🎯 VERIFICAR se há sub-abas que devem ocultar o sideCard
    const hiddenOnSubTabs = sideCardConfig.hiddenOnSubTabs || [];
    if (hiddenOnSubTabs.includes(this.selectedSubTab)) {
      return false;
    }
    
    return true;
  }

  get sideCardConfig(): any {
    return (this.config as any).sideCard || {};
  }

  get sideCardDisplayFields(): any[] {
    return (this.config as any).sideCard?.data?.displayFields || [];
  }

  /**
   * Alterna o estado de colapso do side card no mobile
   */
  toggleSideCardCollapse(): void {
    this.sideCardCollapsed = !this.sideCardCollapsed;
  }

  /**
   * Inicializa o estado do side card baseado no tamanho da tela
   * Mobile: inicia recolhido (true)
   * Desktop: inicia expandido (false)
   */
  private initializeSideCardState(): void {
    const isMobile = window.innerWidth <= 768;
    this.sideCardCollapsed = isMobile;

    // Escutar mudanças de tamanho da tela
    window.addEventListener('resize', () => {
      const newIsMobile = window.innerWidth <= 768;
      if (newIsMobile !== isMobile) {
        this.sideCardCollapsed = newIsMobile;
      }
    });
  }

  /**
   * 🎯 SETUP CONTROLADO DE CHANGE DETECTION
   * 
   * Só ativa quando o usuário clica em "Editar"
   */
  private setupFormChangeDetection(): void {
    // 🎯 IMPORTANTE: Limpar subscriptions antigas antes de criar novas
    this.formSubscriptions.forEach(sub => sub.unsubscribe());
    this.formSubscriptions = [];
    
    // 1. LISTENER PRINCIPAL - Mudanças gerais do formulário
    const mainFormSubscription = this.form.valueChanges.pipe(
      debounceTime(300),
      distinctUntilChanged()
    ).subscribe(value => {
      // 🚨 PROTEÇÃO: Só processar se estiver realmente em modo de edição
      if (!this.isEditMode) {
        return;
      }
      
      // Só marca como dirty se não foi salvo com sucesso
      if (!this.isSavedSuccessfully) {
        this.isFormDirty = true;
      }
      
      // 🚨 PROTEÇÃO: Não emitir evento se estiver em modo de edição
      // Durante edição ativa, não emitir formChange para evitar ngOnChanges no componente pai
      if (!this.isEditMode) {
        this.formChange.emit({
          valid: this.form.valid,
          data: value
        });
      }
    });

    this.formSubscriptions.push(mainFormSubscription);

    // 2. LISTENERS CUSTOMIZADOS - Campos com onValueChange de todas as sub-abas
    const allFields = this.getAllFieldsFromSubTabs();
    allFields.forEach(field => {
      if (field.onValueChange || field.key === 'address_cep') {
        const control = this.form.get(field.key);
        if (control) {
          const customSubscription = control.valueChanges.pipe(
            debounceTime(field.key === 'address_cep' ? 500 : 300),
            distinctUntilChanged()
          ).subscribe(value => {
            if (this.isEditMode) {
              // Tratamento especial para CEP
              if (field.key === 'address_cep') {
                this.handleCepChange(value);
              }
              
              // Chamar onValueChange se definido
              if (field.onValueChange) {
                field.onValueChange(value, this.form);
              }
            }
          });

          this.formSubscriptions.push(customSubscription);
        }
      }
    });
  }

  // ========================================
  // 🚀 MÉTODOS PARA COMPONENTES DINÂMICOS
  // ========================================

  /**
   * Cria um componente dinâmico baseado na configuração da sub-aba
   */
  createDynamicComponent(subTab: SubTabConfig, containerRef: ViewContainerRef): void {
    if (!subTab.dynamicComponent) {
      return;
    }

    const config = subTab.dynamicComponent;
    console.log(`🚀 [CRIANDO] Componente ${config.selector} para sub-aba: ${subTab.id}`);
    
    // Preparar inputs
    const inputs = { ...config.inputs };
    
    // 🎯 RESOLVER INPUTS DINÂMICOS - substitui placeholders pelos valores reais
    Object.keys(inputs).forEach(key => {
      const value = inputs[key];
      
      // ✅ NOVO: Resolver funções nos inputs (incluindo objetos aninhados)
      if (typeof value === 'function') {
        try {
          inputs[key] = value();
          console.log(`🚀 [FUNCTION INPUT] ${key}: função executada = ${inputs[key]}`);
        } catch (error) {
          console.error(`❌ [FUNCTION INPUT] Erro ao executar função para ${key}:`, error);
          inputs[key] = null;
        }
      }
      // ✅ NOVO: Resolver funções dentro de objetos (como config.vehicleId)
      else if (typeof value === 'object' && value !== null) {
        Object.keys(value).forEach(nestedKey => {
          const nestedValue = value[nestedKey];
          if (typeof nestedValue === 'function') {
            try {
              value[nestedKey] = nestedValue();
              console.log(`🚀 [NESTED FUNCTION] ${key}.${nestedKey}: função executada = ${value[nestedKey]}`);
            } catch (error) {
              console.error(`❌ [NESTED FUNCTION] Erro ao executar função para ${key}.${nestedKey}:`, error);
              value[nestedKey] = null;
            }
          }
        });
      }
      // ✅ EXISTENTE: Resolver propriedades dos dados da aba
      else if (this.tabItem?.data && typeof value === 'string' && this.tabItem.data.hasOwnProperty(key)) {
        inputs[key] = this.tabItem.data[value];
        console.log(`📊 [PROP] ${key}: ${value} = ${inputs[key]}`);
      }
    });
    
    // Adicionar dados iniciais se configurado
    if (config.dataBinding?.getInitialData) {
      let initialData: any = null;
      
      console.log(`🔥 [DEBUG] Sub-aba: ${subTab.id}`);
      console.log(`🔥 [DEBUG] getInitialData tipo:`, typeof config.dataBinding.getInitialData);
      console.log(`🔥 [DEBUG] getInitialData valor:`, config.dataBinding.getInitialData);
      
      // 🎯 SUPORTE HÍBRIDO: String ou Função
      if (typeof config.dataBinding.getInitialData === 'function') {
        console.log(`🚀 [DADOS] Executando função direta para ${subTab.id}`);
        initialData = config.dataBinding.getInitialData();
        console.log(`🚀 [DADOS] Resultado da função:`, initialData);
      } else if (typeof config.dataBinding.getInitialData === 'string') {
        console.log(`🔍 [DADOS] Tentando chamar método: ${config.dataBinding.getInitialData}`);
        console.log(`🔍 [DADOS] Contexto disponível:`, Object.getOwnPropertyNames(this));
        console.log(`🔍 [DADOS] Tipo do contexto:`, typeof (this as any)[config.dataBinding.getInitialData]);
        
        // 🎯 VERIFICAR se o método existe no contexto atual
        if (typeof (this as any)[config.dataBinding.getInitialData] === 'function') {
          console.log(`✅ [DADOS] Método encontrado! Executando...`);
          initialData = (this as any)[config.dataBinding.getInitialData]();
          console.log(`✅ [DADOS] Resultado do método:`, initialData);
        } else {
          console.warn(`⚠️ [DADOS] Método '${config.dataBinding.getInitialData}' não encontrado no contexto`);
          console.warn(`🔍 [DADOS] Métodos disponíveis:`, Object.getOwnPropertyNames(this).filter(prop => typeof (this as any)[prop] === 'function'));
        }
      }
      
      if (initialData !== null) {
        inputs['initialData'] = initialData;
        console.log(`📊 [DADOS] Dados iniciais para ${subTab.id}:`, initialData);
      } else {
        console.warn(`⚠️ [DADOS] Nenhum dado inicial obtido para ${subTab.id}`);
      }
    }

    // Preparar outputs
    const outputs: { [key: string]: (event: any) => void } = {};
    if (config.outputs) {
      Object.keys(config.outputs).forEach(eventName => {
        const methodName = config.outputs![eventName];
        if (typeof (this as any)[methodName] === 'function') {
          outputs[eventName] = (event: any) => (this as any)[methodName](event);
        }
      });
    }

    // Criar componente
    const componentRef = this.dynamicComponentResolver.createComponent(
      config.selector,
      containerRef,
      inputs,
      outputs
    );

    if (componentRef) {
      this.dynamicComponentRefs.set(subTab.id, componentRef);
      console.log(`✅ [SUCESSO] Componente ${config.selector} criado para sub-aba: ${subTab.id}`);
      
      // 🚨 CORREÇÃO DEFINITIVA: Executar fora do Zone.js para evitar interferência
      // com FormControls e remote-select
      this.ngZone.runOutsideAngular(() => {
        // Usar microtask (Promise) em vez de macrotask (setTimeout) 
        // para evitar o patchTimer do Zone.js
        Promise.resolve().then(() => {
          // Voltar para a zona do Angular apenas para detectar mudanças
          this.ngZone.run(() => {
            try {
              // 🎯 PROTEÇÃO: Só detectar mudanças se o componente ainda existir
              if (componentRef && !componentRef.hostView.destroyed) {
                componentRef.changeDetectorRef.detectChanges();
                console.log(`🔄 [CDR] Detecção de mudanças aplicada SEM interferência do Zone.js para ${config.selector}`);
              }
            } catch (error) {
              console.warn(`⚠️ [CDR] Erro ao detectar mudanças no componente ${config.selector}:`, error);
            }
          });
        });
      });
    } else {
      console.error(`❌ [ERRO] Falha ao criar componente ${config.selector} para sub-aba: ${subTab.id}`);
    }
  }

  /**
   * Destrói todos os componentes dinâmicos
   */
  private destroyDynamicComponents(): void {
    this.dynamicComponentRefs.forEach((componentRef, subTabId) => {
      componentRef.destroy();
    });
    this.dynamicComponentRefs.clear();
  }

  /**
   * Destrói um componente dinâmico específico
   */
  private destroyDynamicComponent(subTabId: string): void {
    const componentRef = this.dynamicComponentRefs.get(subTabId);
    if (componentRef) {
      console.log(`🗑️ [DESTRUINDO] Componente para sub-aba: ${subTabId}`);
      componentRef.destroy();
      this.dynamicComponentRefs.delete(subTabId);
      console.log(`✅ [DESTRUÍDO] Componente removido para sub-aba: ${subTabId}`);
    } else {
      console.log(`ℹ️ [DESTRUIR] Nenhum componente encontrado para sub-aba: ${subTabId}`);
    }
  }

  /**
   * Verifica se uma sub-aba tem componente dinâmico
   */
  hasDynamicComponent(subTab: SubTabConfig): boolean {
    const hasComponent = !!(subTab.templateType === 'component' && subTab.dynamicComponent);
    console.log(`🔍 [hasDynamicComponent] Sub-aba: ${subTab.id}, templateType: ${subTab.templateType}, dynamicComponent: ${!!subTab.dynamicComponent}, resultado: ${hasComponent}`);
    return hasComponent;
  }

  /**
   * Verifica se um componente dinâmico está válido e ativo
   */
  private isDynamicComponentValid(subTabId: string): boolean {
    const componentRef = this.dynamicComponentRefs.get(subTabId);
    return !!(componentRef && !componentRef.hostView.destroyed);
  }

  /**
   * Obtém estatísticas dos componentes dinâmicos (para debug)
   */
  getDynamicComponentStats(): { total: number; valid: number; destroyed: number } {
    let valid = 0;
    let destroyed = 0;
    
    this.dynamicComponentRefs.forEach((componentRef, subTabId) => {
      if (componentRef.hostView.destroyed) {
        destroyed++;
      } else {
        valid++;
      }
    });
    
    return {
      total: this.dynamicComponentRefs.size,
      valid,
      destroyed
    };
  }

  /**
   * Cria um componente dinâmico para uma sub-aba específica
   */
  private createDynamicComponentForSubTab(subTab: SubTabConfig): void {
    console.log(`🔍 [VERIFICANDO] Sub-aba: ${subTab.id}`);
    
    // 🔍 VERIFICAR se já existe um componente válido para esta sub-aba
    const existingComponent = this.dynamicComponentRefs.get(subTab.id);
    if (existingComponent && !existingComponent.hostView.destroyed) {
      console.log(`⚠️ [PULANDO] Componente já existe para sub-aba: ${subTab.id}`);
      return;
    }

    // 🚨 LIMPAR componente existente se estiver destruído
    if (existingComponent && existingComponent.hostView.destroyed) {
      console.log(`🧹 [LIMPANDO] Componente destruído para sub-aba: ${subTab.id}`);
      this.dynamicComponentRefs.delete(subTab.id);
    }

    console.log(`⏳ [AGUARDANDO] ViewChildren para sub-aba: ${subTab.id}`);
    
    // 🎯 AGUARDAR que o ViewChild seja atualizado
    setTimeout(() => {
      console.log(`🔍 [DEBUG] dynamicComponentOutlet:`, this.dynamicComponentOutlet);
      
      // Verificar se temos ViewContainerRef disponível
      if (!this.dynamicComponentOutlet) {
        console.warn(`⚠️ [ERRO] ViewContainerRef não disponível para sub-aba: ${subTab.id}`);
        console.warn(`🔍 [ERRO] Checando DOM...`);
        
        // DEBUG: Verificar se o elemento existe no DOM
        const dynamicElement = document.querySelector('ng-container[dynamicComponentOutlet]');
        console.log(`🔍 [DOM] dynamic-component element:`, dynamicElement);
        
        return;
      }

      console.log(`📍 [CONTAINER] ViewContainerRef encontrado para sub-aba: ${subTab.id}`);
      console.log(`📍 [CONTAINER] ViewContainerRef details:`, this.dynamicComponentOutlet);
      this.createDynamicComponent(subTab, this.dynamicComponentOutlet);
    }, 50);
  }

  // ========================================
  // 🚀 MÉTODOS PARA COMPONENTES DINÂMICOS DE TESTE
  // ========================================

  /**
   * Método chamado pelo componente de teste dinâmico
   */
  onTestDataChange(data: any): void {
    // Processar dados do componente dinâmico
  }

  /**
   * Método chamado pelo componente de teste dinâmico
   */
  onTestEvent(message: string): void {
    // Processar evento do componente dinâmico
  }

  /**
   * 🔧 UTILITÁRIO: Extrai nome de propriedade de string com placeholders
   * 
   * Suporta diferentes formatos:
   * - {{propertyName}} → propertyName
   * - {propertyName} → propertyName
   * - ${propertyName} → propertyName
   * - [propertyName] → propertyName
   */
  private extractPropertyName(value: string): string {
    // Método 1: Regex para diferentes formatos de placeholder
    const patterns = [
      /^\{\{(.+?)\}\}$/,  // {{propertyName}}
      /^\{(.+?)\}$/,      // {propertyName}
      /^\$\{(.+?)\}$/,    // ${propertyName}
      /^\[(.+?)\]$/       // [propertyName]
    ];

    for (const pattern of patterns) {
      const match = value.match(pattern);
      if (match) {
        return match[1].trim();
      }
    }

    // Método 2: Fallback para slice tradicional ({{propertyName}})
    if (value.startsWith('{{') && value.endsWith('}}')) {
      return value.slice(2, -2).trim();
    }

    // Método 3: Último fallback - retorna o valor original
    return value;
  }

  /**
   * 🔧 UTILITÁRIO: Extrai múltiplas propriedades de uma string
   * 
   * Exemplo: "{{name}} - {{age}} anos" → ['name', 'age']
   */
  private extractMultiplePropertyNames(value: string): string[] {
    const regex = /\{\{(.+?)\}\}/g;
    const matches = [];
    let match;
    
    while ((match = regex.exec(value)) !== null) {
      matches.push(match[1].trim());
    }
    
    return matches;
  }

  /**
   * 🔧 UTILITÁRIO: Resolve valor com múltiplas propriedades
   * 
   * Exemplo: "{{name}} - {{age}} anos" com data {name: 'João', age: 25}
   * Resultado: "João - 25 anos"
   */
  private resolveTemplateString(template: string, data: any): string {
    return template.replace(/\{\{(.+?)\}\}/g, (match, propName) => {
      const cleanPropName = propName.trim();
      return data[cleanPropName] || match; // Se não encontrar, mantém o placeholder
    });
  }

  /**
   * 🔧 UTILITÁRIO: Verifica se uma string é um placeholder
   */
  private isPlaceholder(value: string): boolean {
    const placeholderPatterns = [
      /^\{\{.+\}\}$/,    // {{propertyName}}
      /^\{.+\}$/,        // {propertyName}
      /^\$\{.+\}$/,      // ${propertyName}
      /^\[.+\]$/         // [propertyName]
    ];

    return placeholderPatterns.some(pattern => pattern.test(value));
  }

  // 🎯 NOVO: Configurar listener para campos computados
  private setupComputedFieldsListener(): void {
    if (!this.form) return;
    
    // 🚀 DINÂMICO: Detectar automaticamente todas as dependências dos campos computados
    const allDependencies = this.getAllComputedFieldDependencies();
    
    if (allDependencies.length === 0) {
      console.log('ℹ️ [COMPUTE] Nenhum campo computado encontrado');
      return;
    }
    
    console.log('🎯 [COMPUTE] Dependências detectadas:', allDependencies);
    
    // Escutar mudanças em TODOS os campos que afetam cálculos
    this.form.valueChanges.subscribe(value => {
      // Verificar se algum campo de dependência mudou
      const hasRelevantChange = allDependencies.some(field => 
        value[field] !== undefined && value[field] !== null
      );
      
      if (hasRelevantChange) {
        this.updateComputedFields();
      }
    });
  }

  // 🚀 NOVO: Detectar automaticamente todas as dependências dos campos computados
  private getAllComputedFieldDependencies(): string[] {
    const allFields = this.getAllFieldsFromSubTabs();
    const dependencies = new Set<string>();
    
    allFields.forEach(field => {
      if (field.compute) {
        // 🎯 Dependências explícitas (declaradas pelo desenvolvedor)
        if (field.computeDependencies) {
          field.computeDependencies.forEach(dep => dependencies.add(dep));
        }
        
        // 🚀 DETECÇÃO AUTOMÁTICA: Analisar a função compute para detectar dependências
        const autoDependencies = this.detectDependenciesFromComputeFunction(field);
        autoDependencies.forEach(dep => dependencies.add(dep));
      }
    });
    
    return Array.from(dependencies);
  }

  // 🚀 NOVO: Detectar dependências automaticamente analisando a função compute
  private detectDependenciesFromComputeFunction(field: TabFormField): string[] {
    if (!field.compute) return [];
    
    try {
      const functionString = field.compute.toString();
      const dependencies: string[] = [];
      
      // 🎯 Padrões comuns de acesso a propriedades
      const patterns = [
        /model\.(\w+)/g,           // model.propertyName
        /model\?\.(\w+)/g,         // model?.propertyName
        /model\[['"`](\w+)['"`]\]/g, // model['propertyName']
        /model\[`(\w+)`\]/g        // model[`propertyName`]
      ];
      
      patterns.forEach(pattern => {
        let match;
        while ((match = pattern.exec(functionString)) !== null) {
          const propertyName = match[1];
          if (propertyName && !dependencies.includes(propertyName)) {
            dependencies.push(propertyName);
          }
        }
      });
      
      return dependencies;
    } catch (error) {
      console.warn(`⚠️ Erro ao detectar dependências automáticas para ${field.key}:`, error);
      return [];
    }
  }

  // 🎯 NOVO: Método para recalcular campos computados
  public updateComputedFields(): void {
    if (!this.form) return;
    
    const formValue = this.form.value;
    
    this.getAllFieldsFromSubTabs().forEach(field => {
      if (field.compute) {
        try {
          const computedValue = field.compute(formValue);
          if (computedValue !== undefined && computedValue !== null) {
            this.form.get(field.key)?.setValue(computedValue, { emitEvent: false });
          }
        } catch (error) {
          console.warn(`⚠️ Erro ao recalcular campo ${field.key}:`, error);
        }
      }
    });
  }

  // 🎯 NOVO: Obter valor calculado de um campo
  getComputedValue(field: TabFormField): any {
    if (!field.compute || !this.form) return '';
    
    try {
      const formValue = this.form.value || {};
      return field.compute(formValue);
    } catch (error) {
      console.warn(`⚠️ Erro ao obter valor calculado do campo ${field.key}:`, error);
      return '';
    }
  }

  // 🎯 NOVO: Verificar se um campo deve ser exibido baseado em condições
  shouldShowField(field: TabFormField): boolean {
    // Se não há condição definida, sempre mostrar o campo
    if (!field.conditional || !this.form) {
      return true;
    }

    const { field: conditionalField, value: conditionalValue, operator = 'equals' } = field.conditional;
    const currentValue = this.form.get(conditionalField)?.value;

    // Aplicar a lógica baseada no operador
    switch (operator) {
      case 'equals':
        return currentValue === conditionalValue;
      case 'not-equals':
        return currentValue !== conditionalValue;
      case 'in':
        return Array.isArray(conditionalValue) && conditionalValue.includes(currentValue);
      case 'not-in':
        return Array.isArray(conditionalValue) && !conditionalValue.includes(currentValue);
      default:
        return currentValue === conditionalValue;
    }
  }

  // 🎯 NOVO: Configurar listeners para campos que controlam a visibilidade de outros campos
  private setupConditionalFieldListeners(): void {
    if (!this.form) return;

    const allFields = this.getAllFieldsFromSubTabs();
    const controllerFields = new Set<string>();

    // Encontrar todos os campos que controlam outros campos
    allFields.forEach(field => {
      if (field.conditional?.field) {
        controllerFields.add(field.conditional.field);
      }
    });

    // Configurar listeners para campos controladores
    controllerFields.forEach(fieldKey => {
      const control = this.form.get(fieldKey);
      if (control) {
        control.valueChanges.subscribe(() => {
          // Forçar detecção de mudanças para atualizar a visibilidade dos campos condicionais
          this.cdr.detectChanges();
        });
      }
    });
  }
} 