import {
  Component,
  Input,
  Output,
  EventEmitter,
  OnInit,
  Inject,
  SimpleChanges,
  ChangeDetectorRef,
  OnDestroy,
  AfterViewInit,
  ViewChild,
  ElementRef,
} from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
import { MatButtonModule } from "@angular/material/button";
import { MatTooltipModule } from "@angular/material/tooltip";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
import {
  CdkDragDrop,
  moveItemInArray,
  DragDropModule,
} from "@angular/cdk/drag-drop";
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import {
  UserPreferencesService,
  TablePreferences,
} from "../../../domain/session/user-preferences.service";
import { DomainFilterComponent } from "../domain-filter/domain-filter.component";
import { DomainConfig } from "../base-domain/base-domain.component";
import { ApiFilters } from "../../interfaces/domain-filter.interface";
import { BulkAction } from "../../interfaces/bulk-action.interface";
import { BulkActionsComponent } from "../bulk-actions/bulk-actions.component";
import { SearchStateService } from "../../services/search-state.service";

// ✨ NOVO: Interface para configuração do footer
export interface FooterConfig {
  type: 'sum' | 'avg' | 'count' | 'min' | 'max';
  format?: 'currency' | 'number' | 'percentage' | 'default';
  label?: string; // Ex: "Total:", "Média:", "Itens:", etc.
  precision?: number; // Casas decimais para números
}

export interface Column {
  field: string;
  header: string;
  filterable?: boolean;
  sortable?: boolean;
  width?: string;
  date?: boolean;
  label?: (value: any, row?: any) => string;
  allowHtml?: boolean; // ✅ NOVA PROPRIEDADE: Permite renderização de HTML
  search?: boolean; // ✨ NOVO: Indica se o campo aparece nos filtros avançados
  searchType?: 'text' | 'select' | 'date' | 'number' | 'remote-select'; // ✨ NOVO: Tipo do campo de busca
  searchField?: string; // ✨ NOVO: Campo alternativo para busca na API (alias)
  filterField?: string; // ✨ NOVO: Campo específico para filtros (prioridade sobre searchField)
  searchOptions?: Option[]; // ✨ NOVO: Opções para campos select
  remoteConfig?: any; // ✨ NOVO: Configuração para remote-select
  footer?: FooterConfig; // ✨ NOVO: Configuração do footer para esta coluna
}

export interface Option {
  value: any;
  label: string;
  disabled?: boolean;
}

export interface GroupConfig {
  field: string;
  aggregates?: {
    [key: string]: "sum" | "avg" | "count" | "min" | "max";
  };
  expanded?: boolean;
}

export interface TableAction {
  icon: string;
  label: string;
  action: string;
  color?: string;
  show?: (row: any) => boolean;
}

export interface TableConfig {
  columns: Column[];
  pageSize?: number;
  pageSizeOptions?: number[];
  showFirstLastButtons?: boolean;
  groupBy?: GroupConfig[];
  actions?: TableAction[];
}

export interface GroupedRow {
  isGroup: boolean;
  field: string;
  value: any;
  level: number;
  expanded: boolean;
  aggregates: { [key: string]: any };
  children?: any[];
}

export interface AggregateValue {
  key: string;
  value: any;
  type: "sum" | "avg" | "count" | "min" | "max";
}

export interface RequestFilter {
  label: string;
  field: string;
  type?: "text" | "number" | "date";
}

/**
 * 🎯 DATA TABLE COMPONENT - Sistema de Filtros Simplificado
 * 
 * ARQUITETURA DE FILTROS (versão simplificada):
 * ✅ Filtros Diretos das Colunas (tipo Excel) → APENAS LOCAIS (sem backend)
 * ✅ Domain Filter Component → Filtros avançados que vão ao servidor  
 * ✅ Global Filter (barra de pesquisa) → Vai ao servidor quando necessário
 * ❌ Filtros Tradicionais (removidos) → Causavam confusão de UX
 * 
 * IMPORTANTE: Os filtros diretos das colunas NÃO fazem consultas no backend,
 * apenas filtram os dados já carregados na tela (comportamento tipo Excel/planilha).
 */
@Component({
  selector: "app-data-table",
  standalone: true,
  imports: [
    CommonModule,
    FormsModule,
    DragDropModule,
    MatButtonModule,
    MatTooltipModule,
    DomainFilterComponent,
    BulkActionsComponent,
  ],
  providers: [],
  templateUrl: "./data-table.component.html",
  styleUrl: "./data-table.component.scss",
})
export class DataTableComponent implements OnInit, OnDestroy, AfterViewInit {
  @Input() config!: TableConfig;
  @Input() data: any[] = [];
  @Input() showFilters = true;
  @Input() tableId!: string;
  @Input() loading: boolean = false;
  @Input() totalDataItems: number = 0;
  @Input() serverSidePagination: boolean = true;
  @Input() requestFilters: RequestFilter[] = [];
  @Input() showRequestFilters = false;
  @Input() domainConfig?: DomainConfig;
  @Input() bulkActions: BulkAction[] = [];
  
  // ✨ NOVO: Referência direta ao input de pesquisa
  @ViewChild('searchInput', { static: false }) searchInputRef?: ElementRef<HTMLInputElement>;
  @Output() filterRequested = new EventEmitter<{[key: string]: string}>();
  @Output() advancedFiltersChanged = new EventEmitter<ApiFilters>();
  @Output() rowSelectionChanged = new EventEmitter<any[]>();

  @Output() sortChange = new EventEmitter<{
    field: string;
    direction: "asc" | "desc";
  }>();
  @Output() pageChange = new EventEmitter<{ page: number; pageSize: number }>();
  @Output() filterChange = new EventEmitter<{ [key: string]: string }>();
  @Output() columnsChange = new EventEmitter<Column[]>();
  @Output() actionClick = new EventEmitter<{ action: string; data: any }>();

  // Propriedades para paginação
  @Input() currentPage = 1;
  pageSize = 10;
  filteredData: any[] = [];
  pagedData: any[] = [];

  // Outras propriedades
  sortField: string = "";
  sortDirection: "asc" | "desc" = "asc";
  filters: { [key: string]: string } = {};
  private _globalFilter: string = "";
  showColumnsMenu = false;
  
  // ✨ NOVO: Getter/Setter para globalFilter com debug e proteção
  get globalFilter(): string {
    return this._globalFilter;
  }
  
  set globalFilter(value: string) {
    // ✅ PROTEÇÃO: Só alterar se realmente for diferente
    if (this._globalFilter !== value) {
      console.log('🔍 DEBUG globalFilter sendo alterado:', {
        oldValue: this._globalFilter,
        newValue: value,
        preserveSearchConfig: this.domainConfig?.filterConfig?.searchConfig?.preserveSearchOnDataChange,
        caller: new Error().stack?.split('\n')[2]?.trim()
      });
      
      // ✅ PROTEÇÃO ADICIONAL: Verificar se deve preservar o valor
      const shouldPreserve = this.domainConfig?.filterConfig?.searchConfig?.preserveSearchOnDataChange ?? true;
      
      // Se está tentando limpar o valor mas deve preservar, ignorar
      if (shouldPreserve && value === '' && this._globalFilter !== '') {
        console.log('🛡️ BLOQUEANDO limpeza do globalFilter - preserveSearchOnDataChange ativo');
        return;
      }
      
      this._globalFilter = value;
    }
  }
  allColumns: Column[] = [];
  expandedGroups = new Set<string>();
  showGroupingMenu = false;
  
  // ✨ NOVO: Estados de loading específicos
  isSearchingBackend: boolean = false;
  backendSearchMessage: string = "";
  
  // ✨ NOVO: Estados para mensagem de "nenhum resultado"
  showNoResultsMessage: boolean = false;
  
  // ✅ GETTER/SETTER usando SearchStateService (persiste entre recriações)
  get lastSearchTerms(): string {
    if (!this.tableId) return '';
    return this.searchStateService.getLastSearchTerms(this.tableId);
  }
  
  set lastSearchTerms(value: string) {
    if (!this.tableId) return;
    this.searchStateService.setLastSearchTerms(this.tableId, value);
  }
  
  groupingAggregates: {
    [level: number]: {
      [field: string]: "sum" | "avg" | "count" | "min" | "max" | null;
    };
  } = {
    0: {},
    1: {},
  };

  // ✅ NOVOS ESTADOS PARA COLAPSAR AGREGAÇÕES
  aggregatesExpanded: { [level: number]: boolean } = {
    0: false, // Por padrão colapsada para manter interface mais limpa
    1: false
  };

  private columnVisibility = new Map<string, boolean>();
  private originalColumnOrder: string[] = [];
  groupedData: (GroupedRow | any)[] = [];
  isDragging = false;
  private resizing = false;
  private currentColumn: Column | null = null;
  private startX: number = 0;
  private startWidth: number = 0;
  activeFilters: {[key: string]: string} = {};
  movingColumnField: string | null = null; /* ✅ NOVO: Para feedback visual de movimento */
  
  // ✨ NOVO: Propriedades para filtros avançados
  showAdvancedFilters: boolean = false;
  activeAdvancedFiltersCount: number = 0;
  activeAdvancedFilters: ApiFilters = {}; // ✅ NOVO: Armazenar filtros avançados ativos

  // ✨ RE-ADICIONADO: Propriedades para seleção de linhas (essencial para ações em lote)
  selectedRows = new Set<any>();
  selectAllChecked: boolean = false;
  selectAllIndeterminate: boolean = false;

  // ✨ Ações em Lote com Modal
  showBulkActionsModal = false;

  // ✨ NOVO: Filtro direto da coluna
  activeColumnFilter: string | null = null;
  columnFilterValues: { [columnField: string]: any[] } = {};
  columnFilterDisplayValues: { [columnField: string]: {value: any, display: string, rawValue: any}[] } = {};
  columnFilterSelected: { [columnField: string]: Set<any> } = {};
  columnFilterSearch: { [columnField: string]: string } = {};
  
  // ✨ NOVO: Footer com valores calculados
  footerValues: { [columnField: string]: any } = {};
  showFooter: boolean = false;
  
  // ✨ NOVO: Controle de pesquisa com debounce
  private searchSubject = new Subject<string>();
  private searchSubscription?: Subscription;
  
  // ❌ FILTROS TRADICIONAIS DESABILITADOS
  // Para evitar confusão entre filtros diretos das colunas e filtros tradicionais
  // Agora usamos apenas: filtros diretos (locais) + domain-filter (servidor)
  private traditionalFiltersEnabled = false;

  constructor(
    @Inject(UserPreferencesService)
    private userPreferences: UserPreferencesService,
    private sanitizer: DomSanitizer,
    private searchStateService: SearchStateService,
    private cdr: ChangeDetectorRef
  ) {
    this.columnVisibility = new Map<string, boolean>();
    
    // ✅ NOVO: Listener global para fechar filtro de coluna ao clicar fora
    document.addEventListener('click', (event) => {
      if (this.activeColumnFilter) {
        const target = event.target as HTMLElement;
        const isClickInsidePanel = target.closest('.column-filter-panel');
        const isClickOnFilterBtn = target.closest('.column-filter-btn');
        
        if (!isClickInsidePanel && !isClickOnFilterBtn) {
          this.activeColumnFilter = null;
        }
      }
    });
  }

  ngOnInit() {
    // ❌ FORÇAR: Filtros tradicionais sempre desabilitados
    if (!this.traditionalFiltersEnabled) {
      this.showFilters = false;
    }
    
    // ✨ NOVO: Configurar debounce para pesquisa
    this.setupSearchDebounce();
    
    // Inicializa os dados base
    this.filteredData = [...this.data];
    this.allColumns = [...this.config.columns];

    // Inicializa pageSize do config
    this.pageSize = this.config.pageSize || 10;

    if (this.tableId) {
      const savedPreferences = this.userPreferences.getTablePreferences(
        this.tableId
      );

      // Carrega visibilidade e ordem das colunas
      this.originalColumnOrder = this.allColumns.map((col) => col.field);

      if (savedPreferences.columnOrder.length > 0) {
        this.config.columns = this.reorderColumns(savedPreferences.columnOrder);
        this.allColumns = [...this.config.columns];
      }

      this.allColumns.forEach((column) => {
        const isVisible = savedPreferences.visibility[column.field] ?? true;
        this.columnVisibility.set(column.field, isVisible);
      });

      // Carrega configurações de agrupamento e agregação
      if (savedPreferences.grouping?.groups) {
        this.config.groupBy = savedPreferences.grouping.groups;
        if (savedPreferences.grouping.aggregates) {
          this.groupingAggregates = savedPreferences.grouping.aggregates;
        } else {
          this.groupingAggregates = this.initializeGroupingAggregates(
            savedPreferences.grouping.groups
          );
        }
      }

      // Restaura estado dos filtros
      if (savedPreferences.showFilters !== undefined) {
        this.showFilters = savedPreferences.showFilters;
      }
    }

    // Processa os dados iniciais
    this.processGrouping();
    this.updatePagedData();

    // Carregar preferências salvas
    this.loadColumnPreferences();
    
    // ✅ Aplicar larguras das colunas após inicialização completa
    this.applyStoredColumnWidths();
  }

  ngAfterViewInit(): void {
    // ✅ GARANTIR: Sincronização do valor da pesquisa após a view ser inicializada
    this.forceSyncSearchValue();
    
    // ✅ NOVO: Monitorar mudanças externas no input
    this.setupInputProtection();
  }

  /**
   * ✨ NOVO: Configura proteção contra mudanças externas no input
   */
  private setupInputProtection(): void {
    setTimeout(() => {
      // ✅ MÚLTIPLOS SELETORES: Tentar diferentes formas de encontrar o input
      let inputElement: HTMLInputElement | null = null;
      
      // Tentativa 1: Com tableId específico
      if (this.tableId) {
        const container = document.querySelector(`[data-table-id="${this.tableId}"]`);
        if (container) {
          inputElement = container.querySelector('.global-filter-container input') as HTMLInputElement;
        }
      }
      
      // Tentativa 2: Buscar diretamente por classe (fallback)
      if (!inputElement) {
        inputElement = document.querySelector('.global-filter-container input') as HTMLInputElement;
      }
      
      console.log('🔍 DEBUG setupInputProtection:', {
        tableId: this.tableId,
        inputFound: !!inputElement,
        inputValue: inputElement?.value || 'N/A',
        globalFilterValue: this._globalFilter
      });
      
      if (inputElement) {
        // ✅ OBSERVER: Monitorar mudanças no valor do input
        const observer = new MutationObserver((mutations) => {
          mutations.forEach((mutation) => {
            if (mutation.type === 'attributes' && mutation.attributeName === 'value') {
              const currentValue = inputElement!.value;
              if (currentValue !== this._globalFilter) {
                console.log('🔍 DETECTADA mudança externa no input:', {
                  inputValue: currentValue,
                  globalFilterValue: this._globalFilter,
                  shouldPreserve: this.domainConfig?.filterConfig?.searchConfig?.preserveSearchOnDataChange
                });
                
                // Se deve preservar e o valor foi limpo externamente, restaurar
                const shouldPreserve = this.domainConfig?.filterConfig?.searchConfig?.preserveSearchOnDataChange ?? true;
                if (shouldPreserve && currentValue === '' && this._globalFilter !== '') {
                  console.log('🛡️ RESTAURANDO valor após mudança externa');
                  inputElement!.value = this._globalFilter;
                }
              }
            }
          });
        });
        
        // ✅ OBSERVAR: Mudanças nos atributos do input
        observer.observe(inputElement, {
          attributes: true,
          attributeFilter: ['value']
        });
        
        console.log('🔒 Proteção do input configurada para tableId:', this.tableId);
      } else {
        console.warn('⚠️ Input não encontrado para proteção! tableId:', this.tableId);
      }
    }, 200);
  }

  /**
   * ✨ NOVO: Configurar debounce para pesquisa
   */
  private setupSearchDebounce(): void {
    const debounceTimeMs = this.domainConfig?.filterConfig?.searchConfig?.debounceTime ?? 300;
    
    this.searchSubscription = this.searchSubject.pipe(
      debounceTime(debounceTimeMs),
      distinctUntilChanged()
    ).subscribe(searchTerm => {
      console.log('🔍 Executando pesquisa com debounce:', searchTerm);
      this.performSearch();
    });
  }

  /**
   * ✨ NOVO: Executar pesquisa (separado do applyFilter para controle)
   */
  private performSearch(): void {
    const minLength = this.domainConfig?.filterConfig?.searchConfig?.minSearchLength ?? 1;
    const searchTerm = this.globalFilter?.trim() || '';
    
    console.log('🔍 performSearch:', {
      searchTerm,
      searchLength: searchTerm.length,
      minLength,
      shouldSearch: searchTerm.length === 0 || searchTerm.length >= minLength
    });
    
    // Só pesquisar se não há termo ou se o termo tem o mínimo de caracteres
    if (searchTerm.length === 0 || searchTerm.length >= minLength) {
      this.applyFilter();
    } else {
      console.log(`⏳ Aguardando ${minLength} caracteres para pesquisar (atual: ${searchTerm.length})`);
    }
  }

  ngOnDestroy(): void {
    if (this.searchSubscription) {
      this.searchSubscription.unsubscribe();
    }
  }

  /**
   * ✨ NOVO: Força sincronização do valor da pesquisa com o input HTML
   */
  private forceSyncSearchValue(): void {
    setTimeout(() => {
      // ✅ PRIORIDADE 1: Usar ViewChild se disponível
      let inputElement: HTMLInputElement | null = null;
      
      if (this.searchInputRef?.nativeElement) {
        inputElement = this.searchInputRef.nativeElement;
      } else {
        // ✅ FALLBACK: Buscar no DOM se ViewChild não estiver disponível
        if (this.tableId) {
          const container = document.querySelector(`[data-table-id="${this.tableId}"]`);
          if (container) {
            inputElement = container.querySelector('.global-filter-container input') as HTMLInputElement;
          }
        }
        
        if (!inputElement) {
          inputElement = document.querySelector('.global-filter-container input') as HTMLInputElement;
        }
      }
      
      console.log('🔍 DEBUG forceSyncSearchValue:', {
        tableId: this.tableId,
        hasViewChild: !!this.searchInputRef?.nativeElement,
        inputFound: !!inputElement,
        inputValue: inputElement?.value || 'N/A',
        globalFilterValue: this._globalFilter
      });
      
      if (inputElement) {
        if (inputElement.value !== this._globalFilter) {
          console.log('🔧 SINCRONIZANDO input HTML com globalFilter:', {
            inputValue: inputElement.value,
            globalFilterValue: this._globalFilter,
            tableId: this.tableId,
            method: this.searchInputRef?.nativeElement ? 'ViewChild' : 'DOM Query'
          });
          
          // ✅ FORÇAR VALOR: Usar tanto value quanto dispatchEvent
          inputElement.value = this._globalFilter;
          
          // ✅ DISPARAR EVENTO: Para garantir que o Angular detecte a mudança
          const event = new Event('input', { bubbles: true });
          inputElement.dispatchEvent(event);
        }
      } else {
        console.warn('⚠️ Input de pesquisa não encontrado! tableId:', this.tableId);
      }
    }, 50); // Pequeno delay para garantir que o DOM foi atualizado
  }

  /**
   * ✨ NOVO: Manipular mudanças na pesquisa com debounce (via ngModelChange)
   */
  onSearchChange(value: string): void {
    console.log('🔍 onSearchChange:', {
      value,
      currentGlobalFilter: this._globalFilter,
      preserveConfig: this.domainConfig?.filterConfig?.searchConfig?.preserveSearchOnDataChange
    });
    
    // ✅ GARANTIR: Valor está sendo preservado no modelo
    if (this._globalFilter !== value) {
      console.log('🔧 Sincronizando _globalFilter com valor digitado:', value);
      this._globalFilter = value;
    }
    
    // ✅ GARANTIR: Input HTML também tem o valor correto
    this.ensureInputValue(value);
    
    // Apenas enviar para o subject que vai aplicar debounce
    this.searchSubject.next(value);
  }

  /**
   * ✨ NOVO: Garante que o input HTML tenha o valor correto
   */
  private ensureInputValue(expectedValue: string): void {
    setTimeout(() => {
      // ✅ PRIORIDADE 1: Usar ViewChild se disponível
      let inputElement: HTMLInputElement | null = null;
      
      if (this.searchInputRef?.nativeElement) {
        inputElement = this.searchInputRef.nativeElement;
      } else {
        // ✅ FALLBACK: Buscar no DOM se ViewChild não estiver disponível
        if (this.tableId) {
          const container = document.querySelector(`[data-table-id="${this.tableId}"]`);
          if (container) {
            inputElement = container.querySelector('.global-filter-container input') as HTMLInputElement;
          }
        }
        
        if (!inputElement) {
          inputElement = document.querySelector('.global-filter-container input') as HTMLInputElement;
        }
      }
      
      if (inputElement && inputElement.value !== expectedValue) {
        console.log('🔧 CORRIGINDO valor do input HTML:', {
          inputValue: inputElement.value,
          expectedValue,
          tableId: this.tableId,
          method: this.searchInputRef?.nativeElement ? 'ViewChild' : 'DOM Query'
        });
        inputElement.value = expectedValue;
      }
    }, 0);
  }

  ngOnChanges(changes: SimpleChanges) {
    // 🐛 DEBUG: Verificar quando domainConfig é recebido/alterado
    if (changes['domainConfig']) {
      console.log('🔍 DEBUG DataTable ngOnChanges - domainConfig recebido:', {
        previousValue: changes['domainConfig'].previousValue?.filterConfig?.fieldsSearchDefault,
        currentValue: changes['domainConfig'].currentValue?.filterConfig?.fieldsSearchDefault,
        domainConfigComplete: this.domainConfig
      });
      
      // ✨ NOVO: Reconfigurar debounce quando domainConfig muda
      if (this.searchSubscription) {
        this.searchSubscription.unsubscribe();
      }
      this.setupSearchDebounce();
    }
    
    // 🛡️ PROTEÇÃO CONTRA LOOP INFINITO
    // Quando a paginação é server-side, os dados já vêm filtrados.
    // Apenas atualizamos a exibição, sem emitir um novo evento de filtro.
    if (this.serverSidePagination) {
      if (changes['data'] && changes['data'].currentValue !== changes['data'].previousValue) {
        // ✨ NOVO: Preservar valor da pesquisa se configurado
        const preserveSearch = this.domainConfig?.filterConfig?.searchConfig?.preserveSearchOnDataChange ?? true;
        const currentSearchValue = this._globalFilter; // Sempre usar o valor atual

        console.log('🔍 Preservando pesquisa:', {
          preserveSearch,
          currentSearchValue,
          globalFilterBefore: this._globalFilter,
          dataLength: changes['data'].currentValue?.length || 0
        });
        
        // ✅ ESTRATÉGIA ULTRA SIMPLES: NÃO limpar nada automaticamente
        // Deixar lastSearchTerms persistir até clearAllFilters() explícito
        
        // ✨ LIMPAR LOADING: Dados chegaram do backend
        this.hideBackendSearchLoading();
        
        this.filteredData = [...this.data];
        
        // ✅ GARANTIA ABSOLUTA: Sempre preservar o valor da pesquisa
        if (preserveSearch) {
          // ✅ PROTEÇÃO IMEDIATA: Restaurar valor mesmo que seja vazio
          if (currentSearchValue !== this._globalFilter) {
            console.log('🔧 RESTAURANDO valor da pesquisa imediatamente:', {
              currentSearchValue,
              globalFilterAtual: this._globalFilter
            });
            
            // ✅ BYPASS do setter: Atualizar diretamente para evitar bloqueios
            this._globalFilter = currentSearchValue;
          }
          
          // ✅ PROTEÇÃO ADICIONAL: Forçar sincronização após um ciclo
          setTimeout(() => {
            if (this._globalFilter !== currentSearchValue) {
              console.log('🔧 SEGUNDA TENTATIVA de restauração:', currentSearchValue);
              this._globalFilter = currentSearchValue;
            }
            
            // ✅ SINCRONIZAR com o input HTML
            this.forceSyncSearchValue();
          }, 0);
        }
        
        // ✨ NOVO: Verificar se deve mostrar mensagem de "nenhum resultado"
        this.checkForNoResultsMessage();
        
        this.processGrouping();
        this.updatePagedData();
        
        // ✅ Aplicar larguras das colunas apenas quando há dados novos
        if (this.data.length > 0) {
          this.applyStoredColumnWidths();
        }
      }
    } else {
      // Para paginação client-side, o comportamento antigo de aplicar filtros é mantido.
      this.applyFilter();
    }
  }

  get startIndex(): number {
    return (this.currentPage - 1) * this.pageSize;
  }

  get endIndex(): number {
    return Math.min(this.startIndex + this.pageSize, this.totalItems);
  }

  toggleRequestFilters() {
    this.showRequestFilters = !this.showRequestFilters;
  }


  applyRequestFilter(field: string, value: string) {
    this.filterRequested.emit({field, value});
  }

  updateFilter(field: string, value: string) {
    if (value) {
      this.activeFilters[field] = value;
    } else {
      delete this.activeFilters[field];
    }
    this.filterRequested.emit(this.activeFilters);
  }

  formatDate(value: any): string {
    if (!value) return "";

    // ✅ NOVO: Verificar se é uma string de data no formato ISO (YYYY-MM-DD)
    const isDateOnly = typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
    
    if (isDateOnly) {
      // ✅ Para datas puras (sem hora), usar split para evitar timezone
      const [year, month, day] = value.split('-');
      return `${day}/${month}/${year}`;
    }

    // Tenta converter para Date se for string ou já é Date
    const date = typeof value === "string" ? new Date(value) : value;

    if (isNaN(date.getTime())) {
      return String(value);
    }

    // ✅ NOVO: Verificar se tem componente de hora significativo
    const hasTimeComponent = date.getHours() !== 0 || date.getMinutes() !== 0 || date.getSeconds() !== 0;
    
    if (hasTimeComponent) {
      // Formata como datetime: dd/MM/yyyy HH:mm
      return date.toLocaleString("pt-BR", {
        day: "2-digit",
        month: "2-digit",
        year: "numeric",
        hour: "2-digit",
        minute: "2-digit",
      });
    } else {
      // Formata apenas como data: dd/MM/yyyy
      return date.toLocaleDateString("pt-BR", {
        day: "2-digit",
        month: "2-digit",
        year: "numeric",
      });
    }
  }

  handleSort(column: Column) {
    if (!column.sortable || this.isDragging || this.resizing) return;

    // Atualiza direção
    if (this.sortField === column.field) {
      this.sortDirection = this.sortDirection === "asc" ? "desc" : "asc";
    } else {
      this.sortField = column.field;
      this.sortDirection = "asc";
    }

    // Ordena os dados
    const isAsc = this.sortDirection === "asc";
    this.filteredData.sort((a, b) => {
      const valueA = a[column.field];
      const valueB = b[column.field];

      if (typeof valueA === "string") {
        return isAsc
          ? valueA.localeCompare(valueB)
          : valueB.localeCompare(valueA);
      }

      return isAsc ? valueA - valueB : valueB - valueA;
    });

    // Atualiza a visualização
    this.updatePagedData();

    // Emite o evento
    this.sortChange.emit({
      field: this.sortField,
      direction: this.sortDirection,
    });
  }

  changePage(page: number) {
    if (page >= 1 && this.startIndex < this.filteredData.length) {
      this.currentPage = page;
      this.pageChange.emit({ page, pageSize: this.pageSize });
    }
  }

  handleFilterInput(event: Event, field: string) {
    // ❌ DESABILITADO: Filtros tradicionais removidos para simplificar UX
    if (!this.traditionalFiltersEnabled) return;
    
    const target = event.target as HTMLInputElement;
    if (target) {
      this.filters[field] = target.value;
      this.applyFilter();
    }
  }

  applyFilter(): void {
    try {
      if (!this.data) {
        this.filteredData = [];
        return;
      }

      // ✅ PRIORIDADE 1: Filtrar na lista LOCAL primeiro
      this.filteredData = this.globalFilter
        ? this.applyGlobalFiltering(this.data, this.globalFilter)
        : [...this.data];

      // ✨ IMPORTANTE: Filtros diretos das colunas (this.filters) são aplicados
      // APENAS LOCALMENTE quando vêm de filtros diretos
      if (Object.keys(this.filters).length > 0) {
        this.filteredData = this.applyColumnFilters(
          this.filteredData,
          this.filters
        );
      }

      if (!this.serverSidePagination) {
        this.currentPage = 1; // Reset para primeira página ao filtrar
      }

      this.processGrouping();
      this.updatePagedData();

      // ✅ CONDIÇÃO: Só ir ao backend se for filtro global (não filtros diretos das colunas)
      if (this.serverSidePagination && this.shouldGoToBackend()) {
        const shouldSearchBackend = this.shouldSearchInBackend();
        
        if (shouldSearchBackend) {
          // ✨ MOSTRAR LOADING: Indicar busca no backend
          this.showBackendSearchLoading();
          // ✨ NOVO: Salvar termos da pesquisa
          this.saveSearchTerms();
          
          // ✨ NOVO: Construir filtros automáticos se necessário
          const autoFilters = this.buildAutoSearchFilters();
          const filtersToEmit = Object.keys(autoFilters).length > 0 ? autoFilters : this.filters;
          
          console.log('🚀 Emitindo filtros para backend:', {
            originalFilters: this.filters,
            autoFilters,
            filtersToEmit,
            hasAutoSearch: Object.keys(autoFilters).length > 0
          });
          
          this.filterChange.emit(filtersToEmit);
        } else {
          // ✨ LIMPAR LOADING: Se não vai buscar no backend
          this.hideBackendSearchLoading();
        }
      }
    } catch (error) {
      console.error("Error applying filters:", error);
      this.filteredData = [...this.data];
    }
  }

  /**
   * ✨ NOVO: Determina se deve fazer consulta no backend
   * Filtros diretos das colunas NÃO devem ir ao backend
   */
  private shouldGoToBackend(): boolean {
    // ❌ NUNCA ir ao backend se houver filtros diretos das colunas ativos
    if (Object.keys(this.filters).length > 0) {
      return false;
    }
    
    // ✅ Ir ao backend apenas para filtro global (barra de pesquisa)
    return true;
  }

  /**
   * 🎯 NOVO: Determina se deve buscar no backend
   * 
   * Critério ÚNICO:
   * - Lista local vazia após filtros (independente do número de caracteres)
   * - ✨ NOVO: Se há fieldsSearchDefault configurado, usar busca automática
   */
  private shouldSearchInBackend(): boolean {
    // ✅ CRITÉRIO ÚNICO: Lista local deve estar vazia
    // Se a lista atual não exibe resultado, busca no backend
    const hasEmptyResults = this.filteredData.length === 0 && this.hasAnyFilter();
    
    // ✨ NOVO: Se há fieldsSearchDefault configurado e filtro global ativo, sempre buscar no backend
    const hasAutoSearchConfig = (this.domainConfig?.filterConfig?.fieldsSearchDefault?.length ?? 0) > 0;
    const hasGlobalFilter = Boolean(this.globalFilter && this.globalFilter.trim().length > 0);
    
    return hasEmptyResults || (hasAutoSearchConfig && hasGlobalFilter);
  }

  /**
   * 🎯 REFATORADO: Alias para hasAnyActiveFilter() para manter compatibilidade
   */
  private hasAnyFilter(): boolean {
    return this.hasAnyActiveFilter();
  }

  /**
   * ✨ NOVO: Mostra loading de busca no backend
   */
  private showBackendSearchLoading(): void {
    this.isSearchingBackend = true;
    this.backendSearchMessage = "Buscando no banco de dados...";
  }

  /**
   * ✨ NOVO: Esconde loading de busca no backend
   */
  private hideBackendSearchLoading(): void {
    this.isSearchingBackend = false;
    this.backendSearchMessage = "";
  }

  /**
   * ✨ NOVO: Salva os termos de pesquisa atuais
   */
  private saveSearchTerms(): void {
    const searchTerms: string[] = [];
    
    // Adiciona filtro global se existir
    if (this.globalFilter && this.globalFilter.trim()) {
      searchTerms.push(`"${this.globalFilter.trim()}"`);
    }
    
    // Adiciona filtros de coluna
    Object.entries(this.filters).forEach(([field, value]) => {
      if (value && typeof value === 'string' && value.trim()) {
        const column = this.config.columns.find(c => c.field === field);
        const columnName = column ? column.header : field;
        searchTerms.push(`${columnName}: "${value.trim()}"`);
      }
    });
    
    this.lastSearchTerms = searchTerms.join(', ');
    

  }

  /**
   * ✨ NOVO: Constrói filtros automáticos baseados em fieldsSearchDefault
   * Quando não há resultados locais, cria filtros para os campos configurados
   */
  private buildAutoSearchFilters(): { [key: string]: string } {
    const autoFilters: { [key: string]: string } = {};
    
    // 🐛 DEBUG: Verificar estrutura do domainConfig
    console.log('🔍 DEBUG buildAutoSearchFilters:', {
      domainConfig: this.domainConfig,
      filterConfig: this.domainConfig?.filterConfig,
      fieldsSearchDefault: this.domainConfig?.filterConfig?.fieldsSearchDefault,
      hasFieldsSearchDefault: Boolean(this.domainConfig?.filterConfig?.fieldsSearchDefault),
      fieldsLength: this.domainConfig?.filterConfig?.fieldsSearchDefault?.length
    });
    
    // Verificar se há configuração de campos padrão
    const fieldsSearchDefault = this.domainConfig?.filterConfig?.fieldsSearchDefault;
    if (!fieldsSearchDefault || fieldsSearchDefault.length === 0) {
      console.log('⚠️ Nenhum fieldsSearchDefault configurado ou array vazio');
      return autoFilters;
    }
    
    // Verificar se há filtro global ativo
    const searchTerm = this.globalFilter?.trim();
    if (!searchTerm) {
      return autoFilters;
    }
    
    // ✅ ESTRATÉGIA: Criar filtros para cada campo configurado em fieldsSearchDefault
    fieldsSearchDefault.forEach(fieldName => {
      // Usar o termo de pesquisa global para cada campo configurado
      autoFilters[fieldName] = searchTerm;
    });
    
    console.log('🔍 Filtros automáticos construídos:', {
      searchTerm,
      fieldsSearchDefault,
      autoFilters
    });
    
    return autoFilters;
  }

  /**
   * ✨ NOVO: Verifica se deve mostrar mensagem de "nenhum resultado"
   */
  private checkForNoResultsMessage(): void {
    // Só mostra mensagem se:
    // 1. Não há dados retornados
    // 2. Houve uma busca no backend (lastSearchTerms existe)
    // 3. Está usando paginação server-side
    // 4. ✅ CORREÇÃO: Não está carregando dados
    const hasNoData = this.data.length === 0;
    const hasLastSearch = this.tableId ? this.searchStateService.hasLastSearchTerms(this.tableId) : false;
    const notLoading = !this.loading && !this.isSearchingBackend;
    
    this.showNoResultsMessage = this.serverSidePagination && hasNoData && hasLastSearch && notLoading;
    
    console.log('🔍 checkForNoResultsMessage:', {
      serverSidePagination: this.serverSidePagination,
      hasNoData,
      hasLastSearch,
      notLoading,
      showNoResultsMessage: this.showNoResultsMessage,
      dataLength: this.data.length,
      totalItems: this.totalItems
    });
  }

  private applyGlobalFiltering(data: any[], filterValue: string): any[] {
    const lowerFilter = filterValue.toLowerCase();
    return data.filter((item) =>
      Object.keys(item).some((key) => {
        // Encontra a definição da coluna
        const column = this.config.columns.find((c) => c.field === key);

        // Obtém o valor do item
        let itemValue = item[key];

        // Se a coluna tem uma função label, aplica ela ao valor
        if (column?.label) {
          itemValue = column.label(itemValue, item);
        }

        // Converte para string e compara
        return String(itemValue || "")
          .toLowerCase()
          .includes(lowerFilter);
      })
    );
  }

  private applyColumnFilters(
    data: any[],
    filters: { [key: string]: string }
  ): any[] {
    return data.filter((item) =>
      Object.entries(filters).every(([field, value]) => {
        if (!value) return true;

        // Encontra a definição da coluna
        const column = this.config.columns.find((c) => c.field === field);

        // ✅ CORREÇÃO FUNDAMENTAL: Sempre usar valor bruto para comparação
        let itemValue = item[field]; // Valor bruto original
        let displayValue = itemValue; // Para exibição, se necessário

        // Aplicar formatação apenas para exibição, não para comparação
        if (column?.label) {
          displayValue = column.label(itemValue, item);
        }

        // ✨ NOVO: Suporte para filtros baseados em valores selecionados
        if (value.includes('|')) {
          // Filtro de valores múltiplos (do filtro direto da coluna)
          const allowedValues = value.split('|');
          
          // ✅ USAR VALOR BRUTO: Sempre comparar com o valor original do campo
          let compareValue = itemValue; // Valor bruto original
          
          // Formatar valor do item se for data
          if (column?.date && compareValue) {
            compareValue = this.formatDate(compareValue);
          }
          
          // 🐛 DEBUG TEMPORÁRIO para status
          if (field === 'status') {
            console.log('🔍 DEBUG FILTRO STATUS:', {
              field,
              allowedValues,
              rawValue: item[field],
              compareValue,
              formattedValue: itemValue,
              match: allowedValues.includes(String(compareValue))
            });
          }
          
          return allowedValues.includes(String(compareValue));
        } else {
          // Filtro textual tradicional - usar valor bruto, não formatado
          const itemValueStr = String(itemValue || "").toLowerCase();
          const filterValue = value.toLowerCase();
          return itemValueStr.includes(filterValue);
        }
      })
    );
  }

  dropColumn(event: CdkDragDrop<string[]>) {
    if (!event) return;

    moveItemInArray(
      this.config.columns,
      event.previousIndex,
      event.currentIndex
    );

    this.columnsChange.emit(this.config.columns);
    this.savePreferences();
  }

  toggleColumnsMenu() {
    this.showColumnsMenu = !this.showColumnsMenu;
  }

  toggleColumnVisibility(field: string): void {
    const isVisible = !(this.columnVisibility.get(field) ?? true);
    this.columnVisibility.set(field, isVisible);

    if (!isVisible) {
      this.removeGroupingForField(field);
    }

    this.config.columns = this.allColumns.filter(
      (col) => this.columnVisibility.get(col.field) !== false
    );

    this.savePreferences();
    this.columnsChange.emit(this.config.columns);
    this.updatePagedData();
  }

  private removeGroupingForField(field: string): void {
    if (!this.config.groupBy?.length) return;

    const groupIndex = this.config.groupBy.findIndex((g) => g.field === field);
    if (groupIndex === -1) return;

    this.config.groupBy = this.config.groupBy.slice(0, groupIndex);
    for (let i = groupIndex; i < 2; i++) {
      this.groupingAggregates[i] = {};
    }

    if (groupIndex === 0) {
      this.config.groupBy = [];
      this.groupingAggregates = { 0: {}, 1: {} };
    }

    this.processGrouping();
  }

  get visibleColumns(): Column[] {
    return this.config.columns.filter(
      (col) => this.columnVisibility.get(col.field) !== false
    );
  }

  isColumnVisible(field: string): boolean {
    return this.columnVisibility.get(field) !== false;
  }

  updatePagedData(): void {
    const sourceData =
      this.groupedData.length > 0 ? this.groupedData : this.filteredData;

    if (this.serverSidePagination) {
      this.pagedData = [...sourceData];
    } else {
      const startIndex = (this.currentPage - 1) * this.pageSize;
      this.pagedData = sourceData.slice(startIndex, startIndex + this.pageSize);
    }
    
    // ✨ RE-ADICIONADO: Atualizar estado de seleção quando dados mudam
    this.updateSelectAllState();
    
    // ✨ NOVO: Recalcular valores do footer
    this.calculateFooterValues();
  }

  private reorderColumns(order: string[]): Column[] {
    const columnMap = new Map(
      this.config.columns.map((col) => [col.field, col])
    );
    const reorderedColumns: Column[] = [];

    // Primeiro adiciona as colunas na ordem salva
    order.forEach((field) => {
      if (columnMap.has(field)) {
        reorderedColumns.push(columnMap.get(field)!);
        columnMap.delete(field);
      }
    });

    // Adiciona quaisquer novas colunas que não estavam na ordem salva
    columnMap.forEach((column) => reorderedColumns.push(column));

    return reorderedColumns;
  }

  trackByFn(index: number, item: any): any {
    return item.id || index;
  }

  // Método para resetar para a ordem original
  resetColumnOrder() {
    if (this.tableId) {
      this.config.columns = this.reorderColumns(this.originalColumnOrder);
      const preferences: TablePreferences = {
        visibility: Object.fromEntries(this.columnVisibility),
        columnOrder: this.originalColumnOrder,
        columns: this.config.columns.map((col) => ({
          field: col.field,
          width: col.width || "auto",
        })),
        showFilters: this.showFilters,
        grouping: {
          groups: this.config.groupBy || [],
          aggregates: this.groupingAggregates,
        },
      };
      this.userPreferences.saveTablePreferences(this.tableId, preferences);
      this.columnsChange.emit(this.visibleColumns);
    }
  }

  processGrouping(): void {
    this.groupedData = this.config.groupBy?.length
      ? this.createGroups(this.filteredData, this.config.groupBy, 0)
      : [];
    this.updatePagedData();
  }

  private createGroups(
    data: any[],
    groupFields: GroupConfig[],
    level: number
  ): (GroupedRow | any)[] {
    if (level >= groupFields.length) return data;

    const currentField = groupFields[level].field;
    const groups = new Map<any, any[]>();

    // Agrupa os dados
    data.forEach((item) => {
      const key = item[currentField];
      groups.set(key, [...(groups.get(key) || []), item]);
    });

    // Cria linhas de grupo
    return Array.from(groups.entries()).flatMap(([key, items]) => {
      const groupId = `${level}-${currentField}-${key}`;
      const isExpanded = this.expandedGroups.has(groupId);
      const groupRow: GroupedRow = {
        isGroup: true,
        field: currentField,
        value: key,
        level,
        expanded: isExpanded,
        aggregates: this.calculateAggregates(
          items,
          groupFields[level].aggregates || {}
        ),
        children: this.createGroups(items, groupFields, level + 1),
      };

      return isExpanded ? [groupRow, ...groupRow.children!] : [groupRow];
    });
  }

  private calculateAggregates(
    data: any[],
    aggregates: { [key: string]: string }
  ): { [key: string]: any } {
    const result: { [key: string]: any } = {};

    Object.entries(aggregates).forEach(([field, type]) => {
      switch (type) {
        case "sum":
          result[field] = data.reduce(
            (sum, item) => sum + (Number(item[field]) || 0),
            0
          );
          break;
        case "avg":
          result[field] =
            data.reduce((sum, item) => sum + (Number(item[field]) || 0), 0) /
            data.length;
          break;
        case "count":
          result[field] = data.length;
          break;
        case "min":
          result[field] = Math.min(
            ...data.map((item) => Number(item[field]) || 0)
          );
          break;
        case "max":
          result[field] = Math.max(
            ...data.map((item) => Number(item[field]) || 0)
          );
          break;
      }
    });

    return result;
  }

  toggleGroup(group: GroupedRow) {
    const groupId = `${group.level}-${group.field}-${group.value}`;
    if (this.expandedGroups.has(groupId)) {
      this.expandedGroups.delete(groupId);
    } else {
      this.expandedGroups.add(groupId);
    }
    this.processGrouping();
  }

  getAggregates(row: GroupedRow): AggregateValue[] {
    if (!row.aggregates) return [];

    const groupConfig = this.config.groupBy?.find((g) => g.field === row.field);

    return Object.entries(row.aggregates).map(([key, value]) => {
      const aggregateType =
        groupConfig?.aggregates?.[key] || this.getAggregateType(key);
      const baseField = this.getBaseField(key);

      return {
        key: baseField,
        value: value,
        type: aggregateType,
      };
    });
  }

  private getAggregateType(
    key: string
  ): "sum" | "avg" | "count" | "min" | "max" {
    const aggregateMatch = key.match(/(sum|avg|count|min|max)$/i);
    if (aggregateMatch) {
      return aggregateMatch[1].toLowerCase() as
        | "sum"
        | "avg"
        | "count"
        | "min"
        | "max";
    }

    // Se não encontrar no select, tenta inferir pelo nome do campo
    const key_lower = key.toLowerCase();
    if (key_lower.includes("_sum")) return "sum";
    if (key_lower.includes("_avg")) return "avg";
    if (key_lower.includes("_count")) return "count";
    if (key_lower.includes("_min")) return "min";
    if (key_lower.includes("_max")) return "max";

    if (key_lower.includes("min")) return "min";
    if (key_lower.includes("max")) return "max";
    if (key_lower.includes("avg")) return "avg";
    if (key_lower.includes("count")) return "count";

    console.log("Defaulting to sum for key:", key);
    return "sum";
  }

  private getBaseField(key: string): string {
    const suffixes = ["_sum", "_avg", "_count", "_min", "_max"];
    let baseField = key;

    const key_lower = key.toLowerCase();
    suffixes.forEach((suffix) => {
      if (key_lower.endsWith(suffix.toLowerCase())) {
        baseField = key.slice(0, key.length - suffix.length);
      }
    });

    return baseField;
  }

  formatAggregateValue(
    value: any,
    type: "sum" | "avg" | "count" | "min" | "max"
  ): string {
    if (value === null || value === undefined) return "";
    switch (type.toLowerCase()) {
      case "min":
        return `Mínimo: ${value.toLocaleString()}`;
      case "max":
        return `Máximo: ${value.toLocaleString()}`;
      case "avg":
        return `Média: ${value.toLocaleString()}`;
      case "count":
        return `Quantidade: ${value}`;
      case "sum":
        return `Total: ${value.toLocaleString()}`;
      default:
        console.log("Unknown aggregate type:", type);
        return value.toString();
    }
  }

  toggleGroupingMenu() {
    this.showGroupingMenu = !this.showGroupingMenu;
  }

  getGroupByField(level: number): string {
    return this.config.groupBy?.[level]?.field || "";
  }

  updateGrouping(level: number, field: string) {
    if (!this.config.groupBy) {
      this.config.groupBy = [];
    }

    if (!field && level === 0) {
      // Removendo agrupamento principal
      this.config.groupBy = [];
      this.groupingAggregates = {
        0: {},
        1: {},
      };
      this.groupedData = []; // Limpa os dados agrupados
      this.currentPage = 1; // Volta para a primeira página
    } else if (field) {
      const validAggregates: {
        [key: string]: "sum" | "avg" | "count" | "min" | "max";
      } = {};

      Object.entries(this.groupingAggregates[level]).forEach(([key, value]) => {
        if (value) {
          validAggregates[key] = value as
            | "sum"
            | "avg"
            | "count"
            | "min"
            | "max";
        }
      });

      if (this.config.groupBy[level]) {
        this.config.groupBy[level].field = field;
        this.config.groupBy[level].aggregates = validAggregates;
      } else {
        this.config.groupBy[level] = {
          field,
          aggregates: validAggregates,
        };
      }
    } else {
      // Removendo nível específico
      this.config.groupBy = this.config.groupBy.slice(0, level);
      this.groupingAggregates[level] = {};
      if (level === 0) {
        this.groupingAggregates[1] = {};
        this.groupedData = []; // Limpa os dados agrupados se remover o primeiro nível
      }
    }

    this.savePreferences();
    this.processGrouping();
    this.updatePagedData();
  }

  updateAggregates(level: number) {
    if (this.config.groupBy?.[level]) {
      const validAggregates: {
        [key: string]: "sum" | "avg" | "count" | "min" | "max";
      } = {};

      Object.entries(this.groupingAggregates[level]).forEach(
        ([field, type]) => {
          if (type) {
            validAggregates[field] = type as
              | "sum"
              | "avg"
              | "count"
              | "min"
              | "max";
          }
        }
      );

      this.config.groupBy[level].aggregates = validAggregates;
      this.savePreferences();
      this.processGrouping();
    }
  }

  // Retorna as colunas disponíveis para seleção em um determinado nível
  getAvailableColumns(level: number): Column[] {
    const selectedFields =
      this.config.groupBy?.slice(0, level).map((g) => g.field) || [];
    return this.config.columns.filter(
      (col) => !selectedFields.includes(col.field)
    );
  }

  // Verifica se uma coluna já está selecionada em algum nível anterior
  isColumnSelected(field: string, currentLevel: number): boolean {
    if (!this.config.groupBy) return false;

    // Verifica apenas os níveis anteriores ao atual
    for (let i = 0; i < currentLevel; i++) {
      if (this.config.groupBy[i]?.field === field) {
        return true;
      }
    }
    return false;
  }

  private initializeGroupingAggregates(groups: GroupConfig[]) {
    const aggregates: {
      [level: number]: {
        [field: string]: "sum" | "avg" | "count" | "min" | "max" | null;
      };
    } = {
      0: {},
      1: {},
    };

    groups.forEach((group, index) => {
      if (group.aggregates) {
        aggregates[index] = { ...group.aggregates };
      }
    });

    return aggregates;
  }

  private savePreferences() {
    if (!this.tableId) return;

    const preferences: TablePreferences = {
      visibility: Object.fromEntries(this.columnVisibility),
      columnOrder: this.config.columns.map((col) => col.field),
      columns: this.config.columns.map((col) => ({
        field: col.field,
        width: col.width || "auto",
      })),
      showFilters: this.showFilters,
      grouping: {
        groups: this.config.groupBy || [],
        aggregates: this.groupingAggregates,
      },
    };

    this.userPreferences.saveTablePreferences(this.tableId, preferences);
  }

  onPageChange(page: number) {
    this.currentPage = page;
    if (this.serverSidePagination) {
      // Emite evento para o servidor lidar com a paginação
      this.pageChange.emit({
        page: this.currentPage,
        pageSize: this.pageSize,
      });
    } else {
      // Paginação no cliente
      this.updatePagedData();
    }
  }

  get totalPages(): number {
    return Math.ceil(this.totalItems / this.pageSize);
  }

  get totalItems(): number {
    if (this.serverSidePagination) {
      // ✅ CORREÇÃO: Se não há dados e há filtros ativos, mostrar 0
      if (this.data.length === 0 && this.hasAnyActiveFilter()) {
        return 0;
      }
      // ✅ CASO PADRÃO: Usar totalDataItems do backend
      return this.totalDataItems > 0 ? this.totalDataItems : this.filteredData.length;
    }
    return this.groupedData.length > 0
      ? this.groupedData.length
      : this.filteredData.length;
  }

  /**
   * ✨ NOVO: Verifica se há filtros ativos (global ou de coluna)
   */
  hasAnyActiveFilter(): boolean {
    const hasGlobalFilter = this.globalFilter && this.globalFilter.trim().length > 0;
    const hasColumnFilters = Object.values(this.filters).some((value: any) => 
      value && typeof value === 'string' && value.trim().length > 0
    );
    const hasAdvancedFilters = this.activeAdvancedFiltersCount > 0;
    return hasGlobalFilter || hasColumnFilters || hasAdvancedFilters;
  }

  /**
   * ✨ NOVO: Verifica se deve mostrar mensagem de tabela vazia
   */
  get shouldShowEmptyMessage(): boolean {
    // Só mostrar se:
    // 1. Não há dados para exibir
    // 2. Não está carregando
    // 3. Não está buscando no backend
    // 4. Não há mensagem de "nenhum resultado" (que é específica para buscas no backend)
    const hasNoData = this.pagedData.length === 0;
    const notLoading = !this.loading && !this.isSearchingBackend;
    const noBackendSearchMessage = !this.showNoResultsMessage;
    
    return hasNoData && notLoading && noBackendSearchMessage;
  }

  /**
   * ✨ NOVO: Retorna o número total de colunas (incluindo ações)
   */
  getTotalColumnsCount(): number {
    let count = this.visibleColumns.length;
    if (this.config.actions?.length) {
      count += 1; // Coluna de ações
    }
    return count;
  }

  exportToCsv() {
    // Prepara os dados para exportação
    const data = this.prepareDataForExport();

    // Cria o conteúdo CSV
    const headers = this.visibleColumns.map((col) => col.header).join(",");
    const rows = data.map((row) => {
      return this.visibleColumns
        .map((col) => this.formatCsvValue(row[col.field], col))
        .join(",");
    });

    const csvContent = [headers, ...rows].join("\n");

    // Cria e dispara o download
    const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
    const link = document.createElement("a");
    const url = URL.createObjectURL(blob);

    link.setAttribute("href", url);
    link.setAttribute("download", `${this.tableId || "table"}_export.csv`);
    link.style.visibility = "hidden";

    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }

  private prepareDataForExport(): any[] {
    if (this.groupedData.length > 0) {
      // Se tiver agrupamento, aplana os dados
      return this.flattenGroupedData(this.groupedData);
    }
    return this.filteredData;
  }

  private flattenGroupedData(groupedData: (GroupedRow | any)[]): any[] {
    const result: any[] = [];

    groupedData.forEach((row) => {
      if (!row.isGroup) {
        result.push(row);
      } else if (row.children) {
        result.push(...this.flattenGroupedData(row.children));
      }
    });

    return result;
  }

  private formatCsvValue(value: any, column?: Column): string {
    if (value === null || value === undefined) {
      return "";
    }

    // Formata datas se a coluna for do tipo date
    if (column?.date) {
      value = this.formatDate(value);
    }

    const stringValue = value.toString();

    // Escapa aspas duplas e envolve em aspas se necessário
    if (
      stringValue.includes(",") ||
      stringValue.includes('"') ||
      stringValue.includes("\n")
    ) {
      return `"${stringValue.replace(/"/g, '""')}"`;
    }

    return stringValue;
  }

  toggleFilters() {
    // ❌ DESABILITADO: Filtros tradicionais removidos para simplificar UX
    if (!this.traditionalFiltersEnabled) return;
    
    this.showFilters = !this.showFilters;
    // Opcional: Salvar preferência do usuário
    if (this.tableId) {
      const preferences = this.userPreferences.getTablePreferences(
        this.tableId
      );
      preferences.showFilters = this.showFilters;
      this.userPreferences.saveTablePreferences(this.tableId, preferences);
    }
  }

  getMin(a: number, b: number): number {
    return Math.min(a, b);
  }

  hasActiveGrouping(): boolean {
    return Boolean(this.config?.groupBy && this.config.groupBy.length > 0);
  }

  getColumnHeader(fieldName: string): string {
    const column = this.config.columns.find((col) => col.field === fieldName);
    return column ? column.header : fieldName;
  }

  onActionClick(action: string, row: any) {
    this.actionClick.emit({ action, data: row });
  }

  startResize(event: MouseEvent | TouchEvent, column: Column) {
    event.preventDefault();
    event.stopPropagation();
    
    // ✅ PREVENIR: Seleção de texto durante resize
    document.body.style.userSelect = 'none';
    document.body.style.cursor = 'col-resize';
    
    this.resizing = true;
    this.currentColumn = column;

    if (event instanceof MouseEvent) {
      this.startX = event.pageX;
    } else {
      this.startX = event.touches[0].pageX;
    }

    const columnEl = (event.target as HTMLElement).closest("th");
    if (columnEl) {
      this.startWidth = columnEl.offsetWidth;
      
      // ✅ FEEDBACK VISUAL: Adicionar classe de resize ativo
      columnEl.classList.add('resizing-active');
      columnEl.style.transition = 'none'; // Desabilitar transições durante resize
    }

    // ✅ NOVO: Ativar modo de redimensionamento dinâmico
    this.enableResizeMode();

    document.addEventListener("mousemove", this.resize);
    document.addEventListener("touchmove", this.resize);
    document.addEventListener("mouseup", this.stopResize);
    document.addEventListener("touchend", this.stopResize);
  }

  private resize = (event: MouseEvent | TouchEvent) => {
    if (!this.resizing || !this.currentColumn) return;

    // ✅ PERFORMANCE: Usar requestAnimationFrame para suavizar
    requestAnimationFrame(() => {
      const currentX =
        event instanceof MouseEvent ? event.pageX : event.touches[0].pageX;
      const diff = currentX - this.startX;
      let newWidth = Math.max(80, this.startWidth + diff); // Mínimo de 80px

      // ✅ SNAP AUTOMÁTICO: Para larguras comuns (como Excel)
      const snapWidths = [100, 120, 150, 180, 200, 250, 300, 350, 400, 500];
      const snapThreshold = 10; // pixels de tolerância para snap
      
      for (const snapWidth of snapWidths) {
        if (Math.abs(newWidth - snapWidth) <= snapThreshold) {
          newWidth = snapWidth;
          break;
        }
      }

      if (this.currentColumn) {
        this.currentColumn.width = `${newWidth}px`;
      }
      
      // ✅ NOVO: Aplicar largura dinamicamente com expansão horizontal
      this.applyColumnWidth(newWidth);
    });
  };

  /**
   * ✅ CORRIGIDO: Aplica largura apenas da coluna específica sendo redimensionada
   */
  private applyColumnWidth(newWidth: number): void {
    if (!this.currentColumn) return;

    const columnField = this.currentColumn.field;
    
    // ✅ Aplicar largura no header específico
    const headerEl = document.querySelector(`th[data-field="${columnField}"]`) as HTMLElement;
    if (headerEl) {
      headerEl.style.width = `${newWidth}px`;
      headerEl.style.minWidth = `${newWidth}px`;
    }
    
    // ✅ MÉTODO MAIS PRECISO: Aplicar largura nas células da mesma coluna
    const columnIndex = this.visibleColumns.findIndex(col => col.field === columnField);
    if (columnIndex !== -1) {
      // Ajustar índice considerando coluna de ações
      const adjustedIndex = this.config.actions?.length ? columnIndex + 1 : columnIndex;
      
      // ✅ SELETOR MAIS ESPECÍFICO: Buscar dentro da tabela do componente atual
      const tableContainer = document.querySelector('.data-table-container table');
      if (tableContainer) {
        const cells = tableContainer.querySelectorAll(`tbody tr td:nth-child(${adjustedIndex + 1})`);
        
        cells.forEach(cell => {
          (cell as HTMLElement).style.width = `${newWidth}px`;
          (cell as HTMLElement).style.minWidth = `${newWidth}px`;
        });
      }
    }
    
    // ✅ Atualizar largura total da tabela
    this.updateTableWidth();
  }

  /**
   * ✅ NOVO: Atualiza largura total da tabela para permitir scroll horizontal
   */
  private updateTableWidth(): void {
    const table = document.querySelector('table') as HTMLElement;
    if (!table) return;
    
    // Calcular largura total baseada nas colunas visíveis
    let totalWidth = 0;
    
    // Largura da coluna de ações (se existe)
    if (this.config.actions?.length) {
      totalWidth += 120; // largura fixa para ações
    }
    
    // Largura das colunas de dados
    this.visibleColumns.forEach(column => {
      const width = column.width ? parseInt(column.width.replace('px', '')) : 150;
      totalWidth += width;
    });
    
    // Aplicar largura mínima para forçar scroll horizontal quando necessário
    const minWidth = Math.max(800, totalWidth);
    table.style.width = `${minWidth}px`;
    table.style.minWidth = `${minWidth}px`;
  }

  /**
   * ✅ CORRIGIDO: Aplica larguras armazenadas das colunas com seletores específicos
   */
  private applyStoredColumnWidths(): void {
    // ✅ TIMEOUT: Aguardar renderização do DOM
    setTimeout(() => {
      this.visibleColumns.forEach(column => {
        if (column.width) {
          const width = parseInt(column.width.replace('px', ''));
          const columnField = column.field;
          
          // ✅ Aplicar no header específico deste componente
          const headerEl = document.querySelector(`th[data-field="${columnField}"]`) as HTMLElement;
          if (headerEl) {
            headerEl.style.width = `${width}px`;
            headerEl.style.minWidth = `${width}px`;
          }
          
          // ✅ Aplicar nas células usando seletor mais específico
          const columnIndex = this.visibleColumns.findIndex(col => col.field === columnField);
          if (columnIndex !== -1) {
            const adjustedIndex = this.config.actions?.length ? columnIndex + 1 : columnIndex;
            
            // ✅ SELETOR ESPECÍFICO: Apenas dentro da tabela deste componente
            const tableContainer = document.querySelector('.data-table-container table');
            if (tableContainer) {
              const cells = tableContainer.querySelectorAll(`tbody tr td:nth-child(${adjustedIndex + 1})`);
              
              cells.forEach(cell => {
                (cell as HTMLElement).style.width = `${width}px`;
                (cell as HTMLElement).style.minWidth = `${width}px`;
              });
            }
          }
        }
      });
      
      // Atualizar largura total da tabela
      this.updateTableWidth();
    }, 50); // Pequeno delay para garantir renderização
  }

  private stopResize = () => {
    if (!this.resizing) return;
    
    // ✅ RESTAURAR: Estilos do body
    document.body.style.userSelect = '';
    document.body.style.cursor = '';
    
    // ✅ FEEDBACK VISUAL: Remover classe de resize ativo APENAS da coluna atual
    if (this.currentColumn) {
      const columnEl = document.querySelector(`th[data-field="${this.currentColumn.field}"]`) as HTMLElement;
      if (columnEl) {
        columnEl.classList.remove('resizing-active');
        columnEl.style.transition = ''; // Restaurar transições
        // ✅ CORRIGIDO: NÃO limpar a largura inline - manter o redimensionamento
      }
    }
    
    // ✅ NOVO: Desativar modo de redimensionamento
    this.disableResizeMode();
    
    this.resizing = false;
    this.currentColumn = null;
    
    // ✅ SALVAR: Preferências após resize
    this.saveColumnPreferences();
    
    document.removeEventListener("mousemove", this.resize);
    document.removeEventListener("touchmove", this.resize);
    document.removeEventListener("mouseup", this.stopResize);
    document.removeEventListener("touchend", this.stopResize);
  };

  private saveColumnPreferences() {
    if (!this.tableId) return;

    const preferences = {
      visibility: Object.fromEntries(this.columnVisibility),
      columnOrder: this.config.columns.map((col) => col.field),
      columns: this.config.columns.map((col) => ({
        field: col.field,
        width: col.width || "auto",
      })),
    };

    this.userPreferences.saveTablePreferences(this.tableId, preferences);
  }

  private loadColumnPreferences() {
    if (!this.tableId) return;

    const savedPreferences = this.userPreferences.getTablePreferences(
      this.tableId
    );
    if (savedPreferences?.columnOrder?.length > 0) {
      this.config.columns = this.reorderColumns(savedPreferences.columnOrder);
      this.allColumns = [...this.config.columns];
    }

    // Carrega as larguras das colunas
    const columnWidths = savedPreferences?.columns || [];
    this.config.columns = this.config.columns.map((col) => {
      const savedWidth = columnWidths.find((p) => p.field === col.field)?.width;
      return {
        ...col,
        width: savedWidth || col.width || "auto",
      };
    });
  }

  /**
   * Métodos para nova paginação modernizada
   */

  /**
   * Altera o número de itens por página
   */
  onPageSizeChange(event: Event): void {
    const target = event.target as HTMLSelectElement;
    const newPageSize = parseInt(target.value, 10);
    
    this.pageSize = newPageSize;
    this.config.pageSize = newPageSize;
    
    // Recalcular página atual para manter os mesmos dados visíveis aproximadamente
    const newPage = Math.floor((this.currentPage - 1) * this.pageSize / newPageSize) + 1;
    
    this.pageChange.emit({ page: newPage, pageSize: newPageSize });
  }

  /**
   * Retorna array de números de páginas para exibição na paginação
   */
  getPageNumbers(): (number | string)[] {
    const totalPages = this.getTotalPages();
    const current = this.currentPage;
    const pages: (number | string)[] = [];

    if (totalPages <= 7) {
      // Se há 7 ou menos páginas, mostra todas
      for (let i = 1; i <= totalPages; i++) {
        pages.push(i);
      }
    } else {
      // Lógica para mostrar páginas com elipses
      if (current <= 4) {
        // Início: 1 2 3 4 5 ... 10
        for (let i = 1; i <= 5; i++) {
          pages.push(i);
        }
        pages.push('...');
        pages.push(totalPages);
      } else if (current >= totalPages - 3) {
        // Final: 1 ... 6 7 8 9 10
        pages.push(1);
        pages.push('...');
        for (let i = totalPages - 4; i <= totalPages; i++) {
          pages.push(i);
        }
      } else {
        // Meio: 1 ... 4 5 6 ... 10
        pages.push(1);
        pages.push('...');
        for (let i = current - 1; i <= current + 1; i++) {
          pages.push(i);
        }
        pages.push('...');
        pages.push(totalPages);
      }
    }

    return pages;
  }

  /**
   * Retorna o número total de páginas
   */
  getTotalPages(): number {
    if (this.serverSidePagination) {
      // ✅ CORREÇÃO: Se totalDataItems é 0 mas há dados na tela, usar dados atuais
      if (this.totalDataItems === 0 && this.data.length > 0) {
        return Math.ceil(this.data.length / this.pageSize);
      }
      // ✅ CORREÇÃO: Garantir pelo menos 1 página se há totalDataItems > 0
      const totalPages = Math.ceil(this.totalDataItems / this.pageSize);
      return totalPages > 0 ? totalPages : (this.data.length > 0 ? 1 : 0);
    } else {
      // ✅ CORREÇÃO MELHORADA: Para client-side, usar totalDataItems se disponível, senão filteredData
      let totalPages = 0;
      
      if (this.totalDataItems > 0) {
        // Se temos totalDataItems, usar ele (mesmo em client-side)
        totalPages = Math.ceil(this.totalDataItems / this.pageSize);
      } else {
        // Fallback para filteredData
        totalPages = Math.ceil(this.filteredData.length / this.pageSize);
      }
      
      return this.filteredData.length > 0 || this.data.length > 0 ? Math.max(1, totalPages) : 0;
    }
  }

  /**
   * Navega diretamente para uma página específica
   */
  onPageJump(event: Event): void {
    const target = event.target as HTMLInputElement;
    const pageNumber = parseInt(target.value, 10);
    const totalPages = this.getTotalPages();

    if (pageNumber >= 1 && pageNumber <= totalPages && pageNumber !== this.currentPage) {
      this.onPageChange(pageNumber);
    } else {
      // Restaurar valor atual se inválido
      target.value = this.currentPage.toString();
    }
  }

  /**
   * ========================================
   * 🎨 MÉTODOS PARA NOVA TELA DE COLUNAS VISÍVEIS
   * ========================================
   */

  /**
   * Fecha todos os modais abertos
   */
  closeModals(): void {
    this.showColumnsMenu = false;
    this.showGroupingMenu = false;
    this.activeColumnFilter = null; // ✨ NOVO: Fechar filtros diretos das colunas
  }

  /**
   * Retorna o número de colunas visíveis
   */
  getVisibleColumnsCount(): number {
    return Array.from(this.columnVisibility.values()).filter(Boolean).length;
  }

  /**
   * Retorna o número de colunas ocultas
   */
  getHiddenColumnsCount(): number {
    return this.allColumns.length - this.getVisibleColumnsCount();
  }

  /**
   * Verifica se todas as colunas estão visíveis
   */
  areAllColumnsVisible(): boolean {
    return this.getVisibleColumnsCount() === this.allColumns.length;
  }

  /**
   * Verifica se há pelo menos uma coluna visível
   */
  hasVisibleColumns(): boolean {
    return this.getVisibleColumnsCount() > 0;
  }

  /**
   * Seleciona todas as colunas
   */
  selectAllColumns(): void {
    this.allColumns.forEach(column => {
      this.columnVisibility.set(column.field, true);
    });
    this.updateVisibleColumns();
  }

  /**
   * Desmarca todas as colunas (mantém pelo menos uma visível)
   */
  deselectAllColumns(): void {
    // Mantém a primeira coluna visível por padrão
    this.allColumns.forEach((column, index) => {
      this.columnVisibility.set(column.field, index === 0);
    });
    this.updateVisibleColumns();
  }

  /**
   * Restaura configuração padrão das colunas
   */
  resetToDefault(): void {
    // Reset da visibilidade - todas visíveis por padrão
    this.allColumns.forEach(column => {
      this.columnVisibility.set(column.field, true);
    });

    // Reset da ordem das colunas
    this.config.columns = this.reorderColumns(this.originalColumnOrder);
    this.allColumns = [...this.config.columns];

    // Reset de larguras
    this.config.columns.forEach(column => {
      column.width = 'auto';
    });

    this.updateVisibleColumns();
    this.savePreferences();
  }

  /**
   * TrackBy function para otimizar performance da lista de colunas
   */
  trackColumnByField(index: number, column: Column): string {
    return column.field;
  }

  /**
   * Determina o tipo da coluna para exibição
   */
  getColumnType(column: Column): string {
    if (column.date) return 'Data';
    if (column.sortable && column.filterable) return 'Texto';
    if (column.sortable) return 'Ordenável';
    if (column.filterable) return 'Filtrável';
    return '';
  }

  /**
   * Retorna o ícone apropriado para o tipo da coluna
   */
  getColumnTypeIcon(column: Column): string {
    if (column.date) return 'fas fa-calendar-alt';
    if (column.sortable && column.filterable) return 'fas fa-sort-alpha-down';
    if (column.sortable) return 'fas fa-sort';
    if (column.filterable) return 'fas fa-filter';
    return 'fas fa-minus';
  }

  /**
   * Move uma coluna para cima na ordem
   */
  moveColumnUp(field: string): void {
    // ✅ CORREÇÃO: Mover na lista visual (allColumns) para feedback imediato
    const allColumnsIndex = this.allColumns.findIndex(col => col.field === field);
    if (allColumnsIndex > 0) {
      moveItemInArray(this.allColumns, allColumnsIndex, allColumnsIndex - 1);
    }
    
    // Mover no config.columns (para persistência e grid)
    const currentIndex = this.config.columns.findIndex(col => col.field === field);
    if (currentIndex > 0) {
      moveItemInArray(this.config.columns, currentIndex, currentIndex - 1);
      this.savePreferences();
      this.columnsChange.emit(this.config.columns);
    }
  }

  /**
   * Move uma coluna para baixo na ordem
   */
  moveColumnDown(field: string): void {
    // ✅ CORREÇÃO: Mover na lista visual (allColumns) para feedback imediato
    const allColumnsIndex = this.allColumns.findIndex(col => col.field === field);
    if (allColumnsIndex < this.allColumns.length - 1) {
      moveItemInArray(this.allColumns, allColumnsIndex, allColumnsIndex + 1);
    }
    
    // Mover no config.columns (para persistência e grid)
    const currentIndex = this.config.columns.findIndex(col => col.field === field);
    if (currentIndex < this.config.columns.length - 1) {
      moveItemInArray(this.config.columns, currentIndex, currentIndex + 1);
      this.savePreferences();
      this.columnsChange.emit(this.config.columns);
    }
  }

  /**
   * Aplica as mudanças nas colunas e fecha o modal
   */
  applyColumnChanges(): void {
    this.updateVisibleColumns();
    this.savePreferences();
    this.showColumnsMenu = false;
    this.columnsChange.emit(this.visibleColumns);
  }

  /**
   * Atualiza as colunas visíveis baseado na configuração atual
   */
  private updateVisibleColumns(): void {
    this.config.columns = this.allColumns.filter(
      col => this.columnVisibility.get(col.field) !== false
    );
    this.updatePagedData();
  }

  // ✅ NOVOS MÉTODOS PARA ENHANCED GROUPING PANEL

  /**
   * Retorna o número de níveis de agrupamento ativos
   */
  getActiveGroupingLevels(): number {
    if (!this.config.groupBy) return 0;
    return this.config.groupBy.filter(group => group.field).length;
  }

  /**
   * Remove todo o agrupamento
   */
  clearAllGrouping(): void {
    this.config.groupBy = [];
    this.groupingAggregates = { 0: {}, 1: {} };
    this.expandedGroups.clear();
    this.processGrouping();
    this.savePreferences();
  }

  /**
   * Restaura o agrupamento para configuração padrão
   */
  resetGroupingToDefault(): void {
    this.config.groupBy = [];
    this.groupingAggregates = { 0: {}, 1: {} };
    this.expandedGroups.clear();
    this.processGrouping();
    this.savePreferences();
  }

  /**
   * Retorna apenas as colunas numéricas (para agregações)
   */
  getNumericColumns(): Column[] {
    return this.config.columns.filter(column => {
      // Considera como numérica se não for data e não tiver função label customizada
      return !column.date && !column.label;
    });
  }

  /**
   * Conta o total de agregações configuradas
   */
  getTotalAggregates(): number {
    let total = 0;
    for (let level = 0; level <= 1; level++) {
      const aggregates = this.groupingAggregates[level];
      if (aggregates) {
        total += Object.values(aggregates).filter(value => value !== null && value !== undefined).length;
      }
    }
    return total;
  }

  /**
   * Conta agregações de um nível específico
   */
  getAggregatesCountForLevel(level: number): number {
    const aggregates = this.groupingAggregates[level];
    if (!aggregates) return 0;
    return Object.values(aggregates).filter(value => value !== null && value !== undefined).length;
  }

  /**
   * Aplica as mudanças de agrupamento e fecha o modal
   */
  applyGroupingChanges(): void {
    this.processGrouping();
    this.savePreferences();
    this.showGroupingMenu = false;
    this.updatePagedData();
  }

  /**
   * Alterna expansão/colapso da seção de agregações
   */
  toggleAggregatesExpansion(level: number): void {
    this.aggregatesExpanded[level] = !this.aggregatesExpanded[level];
  }

  // ========================================
  // ✨ MÉTODOS PARA FILTROS AVANÇADOS
  // ========================================

  /**
   * Alterna a visibilidade dos filtros avançados
   */
  toggleAdvancedFilters(): void {
    this.showAdvancedFilters = !this.showAdvancedFilters;
  }

  /**
   * Manipula mudanças nos filtros avançados
   */
  onAdvancedFiltersChanged(filters: ApiFilters): void {
    console.log('🔍 [DEBUG] DataTableComponent.onAdvancedFiltersChanged called with:', {
      filters,
      filtersType: typeof filters,
      filtersKeys: Object.keys(filters),
      filtersCount: Object.keys(filters).length
    });
    
    this.activeAdvancedFilters = filters; // ✅ NOVO: Armazenar filtros ativos
    this.activeAdvancedFiltersCount = Object.keys(filters).length;
    
    console.log('🔍 [DEBUG] DataTableComponent - Emitting advancedFiltersChanged event');
    this.advancedFiltersChanged.emit(filters);
    console.log('🔍 [DEBUG] DataTableComponent - Event emitted successfully');
  }

  // ========================================
  // ✨ MÉTODOS PARA SELEÇÃO DE LINHAS (RE-ADICIONADOS)
  // ========================================

  /**
   * Alterna seleção de uma linha específica
   */
  toggleRowSelection(row: any): void {
    const rowId = this.getRowId(row);
    
    if (this.selectedRows.has(rowId)) {
      this.selectedRows.delete(rowId);
    } else {
      this.selectedRows.add(rowId);
    }
    
    this.updateSelectAllState();
    this.emitSelectionChange();
  }

  /**
   * Verifica se uma linha está selecionada
   */
  isRowSelected(row: any): boolean {
    return this.selectedRows.has(this.getRowId(row));
  }

  /**
   * Alterna seleção de todas as linhas visíveis
   */
  toggleSelectAll(): void {
    const visibleRows = this.getVisibleDataRows();
    
    if (this.selectAllChecked) {
      // Desmarcar todas
      visibleRows.forEach(row => {
        this.selectedRows.delete(this.getRowId(row));
      });
    } else {
      // Marcar todas
      visibleRows.forEach(row => { 
        this.selectedRows.add(this.getRowId(row));
      });
    }
    
    this.updateSelectAllState();
    this.emitSelectionChange();
  }

  /**
   * Atualiza o estado do checkbox "Selecionar Tudo"
   */
  private updateSelectAllState(): void {
    const visibleRows = this.getVisibleDataRows();
    if (!visibleRows.length) {
      this.selectAllChecked = false;
      this.selectAllIndeterminate = false;
      return;
    }
    
    const selectedVisibleRows = visibleRows.filter(row => 
      this.selectedRows.has(this.getRowId(row))
    );

    this.selectAllChecked = selectedVisibleRows.length === visibleRows.length;
    this.selectAllIndeterminate = selectedVisibleRows.length > 0 && selectedVisibleRows.length < visibleRows.length;
  }

  /**
   * Retorna ID único da linha para seleção
   */
  private getRowId(row: any): any {
    return row.id || row._id || JSON.stringify(row);
  }

  /**
   * Retorna apenas as linhas de dados (não grupos)
   */
  private getVisibleDataRows(): any[] {
    return this.pagedData.filter(row => !row.isGroup);
  }

  /**
   * Retorna array com as linhas selecionadas
   */
  getSelectedRows(): any[] {
    const visibleRows = this.getVisibleDataRows();
    return visibleRows.filter(row => this.selectedRows.has(this.getRowId(row)));
  }

  /**
   * Limpa todas as seleções
   */
  clearSelection(): void {
    this.selectedRows.clear();
    this.updateSelectAllState();
    this.emitSelectionChange();
  }

  /**
   * Emite evento de mudança na seleção
   */
  private emitSelectionChange(): void {
    const selectedRows = this.getSelectedRows();
    this.rowSelectionChanged.emit(selectedRows);
  }

  // ========================================
  // ✨ MÉTODOS PARA AÇÕES EM LOTE (AGORA COM MODAL)
  // ========================================

  toggleBulkActionsModal(): void {
    this.showBulkActionsModal = !this.showBulkActionsModal;
  }

  handleExecuteAction(event: { action: BulkAction; items: any[] }): void {
    if (event.action.action) {
      event.action.action(event.items);
      this.clearSelection(); // Limpa a seleção após a execução
    }
  }

  /**
   * ✨ NOVO: Verifica se há ações que não requerem seleção
   */
  hasActionsWithoutSelection(): boolean {
    return this.bulkActions.some(action => action.requiresSelection === false);
  }

  /**
   * 🔒 SEGURANÇA: Criar HTML seguro para renderização
   * Usado para colunas com allowHtml=true
   */
  getSafeHtml(htmlContent: string): SafeHtml {
    return this.sanitizer.bypassSecurityTrustHtml(htmlContent);
  }

  /**
   * 🎨 Verificar se a coluna deve renderizar HTML
   */
  shouldRenderHtml(column: Column): boolean {
    return !!(column.allowHtml && column.label);
  }

  /**
   * ✨ NOVO: Limpa todos os filtros
   */
  clearAllFilters(): void {
    
    // Limpa filtro global
    this.globalFilter = '';
    
    // Limpa filtros de coluna
    this.filters = {};
    
    // Limpa filtros ativos
    this.activeFilters = {};
    
    // ✨ NOVO: Limpa filtros diretos das colunas
    this.activeColumnFilter = null;
    this.columnFilterSelected = {};
    this.columnFilterSearch = {};
    this.columnFilterDisplayValues = {};
    
    // Limpa mensagem de "nenhum resultado"
    this.showNoResultsMessage = false;
    if (this.tableId) {
      this.searchStateService.clearLastSearchTerms(this.tableId);
    }
    
    // Aplica os filtros limpos
    this.applyFilter();
  }

  // ========================================
  // ✨ MÉTODOS PARA FILTRO DIRETO DA COLUNA
  // ========================================

  /**
   * ✨ NOVO: Abre/fecha filtro direto da coluna
   */
  toggleColumnFilter(column: Column, event?: MouseEvent): void {
    if (event) {
      event.stopPropagation();
      event.preventDefault();
    }

    if (this.activeColumnFilter === column.field) {
      this.activeColumnFilter = null;
    } else {
      this.activeColumnFilter = column.field;
      
      // ✅ CORRIGIDO: Sempre recarregar valores baseados na lista atual
      this.loadColumnFilterValues(column);
      

    }
  }

  /**
   * ✨ NOVO: Carrega valores únicos da coluna (baseado na lista filtrada atual)
   */
  private loadColumnFilterValues(column: Column): void {
    const field = column.field;
    
    // ✅ CORRIGIDO: Usar dados filtrados excluindo o filtro atual
    const sourceData = this.getDataForColumnFilter(field);
    


    
    // Extrair valores únicos da coluna com informações de exibição
    const uniqueDisplayValues = new Map<any, {value: any, display: string, rawValue: any}>();
    
    sourceData.forEach((row: any) => {
      let rawValue = row[field];
      let displayValue = rawValue;
      let filterValue = rawValue;
      
      // Se a coluna tem função label, aplicar ela
      if (column.label) {
        displayValue = column.label(rawValue, row);
        filterValue = rawValue; // Manter valor bruto para filtrar
      }
      
      // Formatar datas se necessário
      if (column.date && rawValue) {
        displayValue = this.formatDate(rawValue);
        filterValue = rawValue; // Manter valor bruto para filtrar
      }
      
              // ✨ CORRIGIDO: Sempre usar valor bruto como key para filtros
        if (rawValue !== null && rawValue !== undefined && rawValue !== '') {
          let displayText = String(displayValue);
          
          // Extrair texto de HTML se necessário
          if (column.allowHtml && typeof displayValue === 'string' && displayValue.includes('<')) {
            displayText = this.extractTextFromHtml(displayValue);
          }
          
          // ✅ NOVA CORREÇÃO: Normalizar valor para colunas de data
          let normalizedValue = rawValue;
          if (column.date) {
            normalizedValue = this.normalizeDateForFilter(rawValue);
            

          }
          
          // ✅ CHAVE CORRIGIDA: Usar valor normalizado para datas, valor bruto para outros
          const keyValue = column.date ? normalizedValue : rawValue;
          
          // 🐛 DEBUG TEMPORÁRIO para status
          if (field === 'status') {
            console.log('🔍 DEBUG EXTRAÇÃO STATUS:', {
              field,
              rawValue,
              displayValue,
              displayText,
              keyValue,
              finalObject: {
                value: keyValue,
                display: displayText,
                rawValue: rawValue
              }
            });
          }
          
          uniqueDisplayValues.set(keyValue, {
            value: keyValue,        // ✅ Valor normalizado/bruto para comparação
            display: displayText,   // ✅ Texto formatado para exibição
            rawValue: rawValue      // ✅ Valor original
          });
        }
    });

    // Converter para arrays e ordenar por display
    const displayValuesArray = Array.from(uniqueDisplayValues.values()).sort((a, b) => {
      return a.display.localeCompare(b.display);
    });

    this.columnFilterDisplayValues[field] = displayValuesArray;
    this.columnFilterValues[field] = displayValuesArray.map(item => item.value);

    // Inicializar seleção (todos selecionados por padrão)
    if (!this.columnFilterSelected[field]) {
      this.columnFilterSelected[field] = new Set(this.columnFilterValues[field]);
    } else {
      // ✅ CORRIGIDO: Sincronizar com novos valores se já existia seleção
      const currentlySelected = Array.from(this.columnFilterSelected[field]);
      const newValidValues = this.columnFilterValues[field].filter(value => 
        currentlySelected.includes(value)
      );
      this.columnFilterSelected[field] = new Set(newValidValues);
    }

    // Inicializar pesquisa
    if (!this.columnFilterSearch[field]) {
      this.columnFilterSearch[field] = '';
    }
  }

  /**
   * ✨ NOVO: Extrai texto de HTML para exibição no filtro
   */
  private extractTextFromHtml(htmlString: string): string {
    try {
      // Criar elemento temporário para extrair texto
      const tempDiv = document.createElement('div');
      tempDiv.innerHTML = htmlString;
      
      // Extrair texto limpo
      const textContent = tempDiv.textContent || tempDiv.innerText || '';
      
      // Limpar elemento temporário
      tempDiv.remove();
      
      return textContent.trim();
    } catch (error) {
      console.warn('Erro ao extrair texto do HTML:', error);
      // Fallback: tentar remover tags básicas com regex
      return htmlString.replace(/<[^>]*>/g, '').trim();
    }
  }

  /**
   * ✨ NOVO: Normaliza valores de data para comparação consistente nos filtros
   */
  private normalizeDateForFilter(dateValue: any): string {
    if (!dateValue) return '';
    
    try {
      let dateStr = String(dateValue).trim();
      
      // Se já está em formato ISO (YYYY-MM-DD), retornar como está
      if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
        return dateStr;
      }
      
      // Se está em formato brasileiro (DD/MM/YYYY), converter para ISO
      if (/^\d{2}\/\d{2}\/\d{4}$/.test(dateStr)) {
        const [day, month, year] = dateStr.split('/');
        return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
      }
      
      // Tentar criar Date object e extrair formato ISO
      const date = new Date(dateValue);
      if (!isNaN(date.getTime())) {
        return date.toISOString().split('T')[0]; // YYYY-MM-DD
      }
      
      // Fallback: retornar string original
      return dateStr;
      
    } catch (error) {
      console.warn('Erro ao normalizar data para filtro:', error, dateValue);
      return String(dateValue);
    }
  }

  /**
   * ✨ NOVO: TrackBy function para itens de valor do filtro
   */
  trackValueItem(index: number, item: {value: any, display: string, rawValue: any}): any {
    return item.value;
  }

  /**
   * ✨ NOVO: Verifica se deve mostrar preview visual do valor
   */
  hasVisualPreview(field: string, item: {value: any, display: string, rawValue: any}): boolean {
    const column = this.config.columns.find(c => c.field === field);
    return !!(column?.allowHtml && column?.label);
  }

  /**
   * ✨ NOVO: Retorna HTML do preview visual
   */
  getValuePreview(field: string, item: {value: any, display: string, rawValue: any}): string {
    const column = this.config.columns.find(c => c.field === field);
    if (column?.label && column?.allowHtml) {
      // Gerar HTML usando a função label original
      const sampleRow = this.data.find(row => row[field] === item.rawValue);
      if (sampleRow) {
        return column.label(item.rawValue, sampleRow);
      }
    }
    return '';
  }

  /**
   * ✨ NOVO: Retorna valores filtrados pela pesquisa
   */
  getFilteredColumnValues(field: string): {value: any, display: string, rawValue: any}[] {
    const searchTerm = (this.columnFilterSearch[field] || '').toLowerCase();
    const allValues = this.columnFilterDisplayValues[field] || [];
    
    if (!searchTerm) {
      return allValues;
    }
    
    return allValues.filter(item => 
      item.display.toLowerCase().includes(searchTerm)
    );
  }

  /**
   * ✨ NOVO: Alterna seleção de um valor
   */
  toggleColumnValue(field: string, value: any): void {
    if (!this.columnFilterSelected[field]) {
      this.columnFilterSelected[field] = new Set();
    }

    const selected = this.columnFilterSelected[field];
    if (selected.has(value)) {
      selected.delete(value);
    } else {
      selected.add(value);
    }
  }

  /**
   * ✨ NOVO: Verifica se valor está selecionado
   */
  isColumnValueSelected(field: string, value: any): boolean {
    return this.columnFilterSelected[field]?.has(value) || false;
  }

  /**
   * ✨ NOVO: Seleciona/deseleciona todos os valores
   */
  toggleSelectAllColumnValues(field: string): void {
    const allValues = this.getFilteredColumnValues(field);
    const selected = this.columnFilterSelected[field] || new Set();
    
    const allSelected = allValues.every(item => selected.has(item.value));
    
    if (allSelected) {
      // Desmarcar todos os valores filtrados
      allValues.forEach(item => selected.delete(item.value));
    } else {
      // Marcar todos os valores filtrados
      allValues.forEach(item => selected.add(item.value));
    }
  }

  /**
   * ✨ NOVO: Verifica se todos os valores estão selecionados
   */
  areAllColumnValuesSelected(field: string): boolean {
    const allValues = this.getFilteredColumnValues(field);
    const selected = this.columnFilterSelected[field] || new Set();
    return allValues.length > 0 && allValues.every(item => selected.has(item.value));
  }

  /**
   * ✨ NOVO: Aplica filtro da coluna (APENAS LOCAL - sem consulta no backend)
   */
  applyColumnFilter(field: string): void {
    const selected = this.columnFilterSelected[field];
    const totalValues = this.getTotalColumnValuesCount(field);
    

    
    if (!selected || selected.size === 0) {
      // Se nenhum valor selecionado, remover filtro
      delete this.filters[field];
    } else if (selected.size === totalValues) {
      // Se todos os valores estão selecionados, remover filtro (não filtrar)
      delete this.filters[field];
    } else {
      // Aplicar filtro baseado nos valores selecionados
      const filterValue = Array.from(selected).join('|');
      this.filters[field] = filterValue;
      

    }
    
    this.activeColumnFilter = null;
    
    // ✅ CORRIGIDO: Aplicar filtros APENAS LOCALMENTE (sem backend)
    this.applyFiltersLocally();
  }

  /**
   * ✨ NOVO: Limpa filtro da coluna
   */
  clearColumnFilter(field: string): void {
    // Remover filtro ativo
    delete this.filters[field];
    
    // Restaurar todos os valores como selecionados
    if (this.columnFilterDisplayValues[field]) {
      const allValues = this.columnFilterDisplayValues[field].map(item => item.value);
      this.columnFilterSelected[field] = new Set(allValues);
    } else if (this.columnFilterValues[field]) {
      // Fallback para compatibilidade
      this.columnFilterSelected[field] = new Set(this.columnFilterValues[field]);
    }
    
    // Limpar pesquisa
    this.columnFilterSearch[field] = '';
    
    // Forçar detecção de mudanças
    this.cdr.detectChanges();
    
    // ✅ CORRIGIDO: Aplicar filtros APENAS LOCALMENTE (sem backend)
    this.applyFiltersLocally();
  }

  /**
   * ✨ NOVO: Aplica filtros apenas localmente (sem emitir eventos para o backend)
   */
  private applyFiltersLocally(): void {
    try {
      if (!this.data) {
        this.filteredData = [];
        return;
      }

      // ✅ FILTRAR NA LISTA LOCAL: Não emite eventos para o backend
      this.filteredData = this.globalFilter
        ? this.applyGlobalFiltering(this.data, this.globalFilter)
        : [...this.data];

      // ✅ CORRIGIDO: Aplicar filtros de coluna um por vez
      Object.entries(this.filters).forEach(([field, filterValue]) => {
        if (filterValue) {
          this.filteredData = this.applySingleColumnFilter(this.filteredData, field, filterValue);
        }
      });

      if (!this.serverSidePagination) {
        this.currentPage = 1; // Reset para primeira página ao filtrar
      }

      this.processGrouping();
      this.updatePagedData();
      
      // ✅ NOVO: Recarregar valores disponíveis para outras colunas (filtros cumulativos)
      this.refreshAvailableValuesForOtherColumns();

      // ❌ REMOVIDO: NÃO emitir eventos para o backend
      // this.filterChange.emit(this.filters);
      
    } catch (error) {
      console.error("Error applying local filters:", error);
      this.filteredData = [...this.data];
    }
  }

  /**
   * ✨ NOVO: Aplica um único filtro de coluna
   */
  private applySingleColumnFilter(data: any[], field: string, filterValue: string): any[] {
    const column = this.config.columns.find(c => c.field === field);
    if (!column) return data;

    if (filterValue.includes('|')) {
      // Filtro de valores múltiplos (do filtro direto da coluna)
      const allowedValues = filterValue.split('|');
      

      
      const result = data.filter(item => {
        // ✅ CORRIGIDO: Usar sempre valor bruto para comparação
        let itemValue = item[field];
        
        // ✅ NOVA CORREÇÃO: Normalizar datas para comparação consistente
        let normalizedItemValue = itemValue;
        let normalizedAllowedValues = allowedValues;
        
        if (column.date && itemValue) {
          // Para colunas de data, normalizar para formato ISO (YYYY-MM-DD)
          normalizedItemValue = this.normalizeDateForFilter(itemValue);
          normalizedAllowedValues = allowedValues.map(val => this.normalizeDateForFilter(val));
          

        }
        
        const finalValue = String(normalizedItemValue);
        const isIncluded = normalizedAllowedValues.includes(finalValue);
        

        
        return isIncluded;
      });
      

      
      return result;
    } else {
      // Filtro textual tradicional
      return data.filter(item => {
        let itemValue = item[field];
        let filterValueToCompare = filterValue;
        
        // ✅ NOVA CORREÇÃO: Para colunas de data, normalizar ambos os valores
        if (column.date) {
          // Normalizar valor do item para ISO
          itemValue = this.normalizeDateForFilter(itemValue);
          // Normalizar valor do filtro para ISO
          filterValueToCompare = this.normalizeDateForFilter(filterValue);
        } else if (column.label) {
          // Para outras colunas, aplicar formatação normal
          itemValue = column.label(itemValue, item);
        }
        
        const itemValueStr = String(itemValue || "").toLowerCase();
        const filterValueStr = filterValueToCompare.toLowerCase();
        return itemValueStr.includes(filterValueStr);
      });
    }
  }

  /**
   * ✨ NOVO: Recarrega valores disponíveis para outras colunas após filtrar
   */
  private refreshAvailableValuesForOtherColumns(): void {
    // ✅ SIMPLIFICADO: Limpar cache de outras colunas para forçar recarga
    Object.keys(this.columnFilterDisplayValues).forEach(field => {
      if (field !== this.activeColumnFilter) {
        // Limpar cache para forçar recarga quando a coluna for aberta novamente
        delete this.columnFilterDisplayValues[field];
        delete this.columnFilterValues[field];
        // Manter seleções existentes para não perder filtros aplicados
      }
    });
    

  }

  /**
   * ✨ NOVO: Retorna dados para filtro de coluna (excluindo o filtro da própria coluna)
   */
  private getDataForColumnFilter(field: string): any[] {
    // Se não há filtros ou se esta é a única coluna filtrada, usar dados filtrados atuais
    const otherFilters = Object.keys(this.filters).filter(f => f !== field);
    
    if (otherFilters.length === 0) {
      // Sem outros filtros, usar dados originais ou com filtro global
      return this.globalFilter
        ? this.applyGlobalFiltering(this.data, this.globalFilter)
        : [...this.data];
    }
    
    // Aplicar apenas os outros filtros (excluindo esta coluna)
    let data = this.globalFilter
      ? this.applyGlobalFiltering(this.data, this.globalFilter)
      : [...this.data];
    
    otherFilters.forEach(otherField => {
      const filterValue = this.filters[otherField];
      if (filterValue) {
        data = this.applySingleColumnFilter(data, otherField, filterValue);
      }
    });
    
    return data;
  }

  /**
   * ✨ NOVO: Verifica se coluna tem filtro ativo
   */
  hasActiveColumnFilter(field: string): boolean {
    return Boolean(this.filters[field]);
  }

  /**
   * ✨ NOVO: Conta quantos valores estão selecionados
   */
  getSelectedColumnValuesCount(field: string): number {
    return this.columnFilterSelected[field]?.size || 0;
  }

  /**
   * ✨ NOVO: Conta total de valores da coluna
   */
  getTotalColumnValuesCount(field: string): number {
    return this.columnFilterDisplayValues[field]?.length || 0;
  }

  /**
   * ✨ NOVO: Conta valores disponíveis na lista original (antes dos filtros)
   */
  getOriginalColumnValuesCount(field: string): number {
    const column = this.config.columns.find(c => c.field === field);
    if (!column) return 0;
    
    const uniqueValues = new Set();
    this.data.forEach(row => {
      // ✅ CORRIGIDO: Usar valor bruto para contagem consistente
      let value = row[field];
      if (value !== null && value !== undefined && value !== '') {
        uniqueValues.add(value);
      }
    });
    
    return uniqueValues.size;
  }

  /**
   * ✨ NOVO: Verifica se há filtros ativos que reduziram as opções
   */
  hasReducedOptions(field: string): boolean {
    const current = this.getTotalColumnValuesCount(field);
    const original = this.getOriginalColumnValuesCount(field);
    return current < original;
  }

  /**
   * ✨ NOVO: Retorna o cabeçalho da coluna ativa
   */
  getActiveColumnHeader(): string {
    if (!this.activeColumnFilter) return '';
    const column = this.config.columns.find(c => c.field === this.activeColumnFilter);
    return column ? column.header : this.activeColumnFilter;
  }

  /**
   * ✨ NOVO: Calcula posição do painel de filtro
   */
  getColumnFilterPosition(): { left: number; top: number } {
    if (!this.activeColumnFilter) {
      return { left: 0, top: 0 };
    }

    try {
      // Encontrar o botão de filtro ativo
      const filterBtn = document.querySelector(`th[data-field="${this.activeColumnFilter}"] .column-filter-btn`) as HTMLElement;
      
      if (!filterBtn) {
        console.warn('Botão de filtro não encontrado para campo:', this.activeColumnFilter);
        return { left: 0, top: 0 };
      }

      const rect = filterBtn.getBoundingClientRect();
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;

      return {
        left: rect.left + scrollLeft,
        top: rect.bottom + scrollTop + 5 // 5px de margem
      };
    } catch (error) {
      console.error('Erro ao calcular posição do filtro:', error);
      return { left: 0, top: 0 };
    }
  }

  // ========================================
  // ✨ MÉTODOS PARA REDIMENSIONAMENTO DINÂMICO
  // ========================================

  /**
   * ✅ NOVO: Ativa modo de redimensionamento dinâmico
   * Permite que a tabela se expanda horizontalmente durante resize
   */
  private enableResizeMode(): void {
    const container = document.querySelector('.data-table-container') as HTMLElement;
    const scrollContainer = document.querySelector('.table-scroll-container') as HTMLElement;
    const table = document.querySelector('table') as HTMLElement;

    if (container) {
      container.classList.add('resizing-mode');
    }
    
    if (scrollContainer) {
      scrollContainer.classList.add('allow-expansion');
    }
    
    if (table) {
      table.classList.add('resizing-active');
    }
  }

  /**
   * ✅ CORRIGIDO: Desativa modo de redimensionamento sem afetar outras colunas
   * Restaura comportamento normal da tabela
   */
  private disableResizeMode(): void {
    const container = document.querySelector('.data-table-container') as HTMLElement;
    const scrollContainer = document.querySelector('.table-scroll-container') as HTMLElement;
    const table = document.querySelector('table') as HTMLElement;

    if (container) {
      container.classList.remove('resizing-mode');
    }
    
    if (scrollContainer) {
      scrollContainer.classList.remove('allow-expansion');
    }
    
    if (table) {
      table.classList.remove('resizing-active');
    }
    
    // ✅ REMOVIDO: Não limpar larguras de todas as colunas
    // Isso estava causando o redimensionamento de todas as colunas
    // Apenas remover classes de resize ativo
    const resizingElements = document.querySelectorAll('.resizing-active');
    resizingElements.forEach(element => {
      element.classList.remove('resizing-active');
    });
  }

  // ========================================
  // ✨ FOOTER COM VALORES CALCULADOS
  // ========================================

  /**
   * Calcula os valores do footer para todas as colunas que possuem configuração de footer
   */
  private calculateFooterValues(): void {
    // Usar dados atuais visíveis (considerando filtros aplicados)
    const dataToCalculate = this.getVisibleDataRows();
    
    // Verificar se alguma coluna tem footer configurado
    const hasFooterConfig = this.visibleColumns.some(col => col.footer);
    this.showFooter = hasFooterConfig && dataToCalculate.length > 0;
    
    if (!this.showFooter) {
      this.footerValues = {};
      return;
    }

    // Calcular valores para cada coluna que tem footer
    this.visibleColumns.forEach(column => {
      if (column.footer) {
        this.footerValues[column.field] = this.calculateColumnFooterValue(
          column, 
          dataToCalculate
        );
      }
    });
  }

  /**
   * Calcula o valor do footer para uma coluna específica
   */
  private calculateColumnFooterValue(column: Column, data: any[]): any {
    if (!column.footer || data.length === 0) {
      return null;
    }

    // Para count, sempre retorna o total de linhas
    if (column.footer.type === 'count') {
      return data.length;
    }

    // Para outros tipos, obter apenas valores numéricos válidos
    const numericValues = data
      .map(row => this.getNumericValue(row[column.field]))
      .filter((value): value is number => value !== null && !isNaN(value));

    if (numericValues.length === 0) {
      return null;
    }

    switch (column.footer.type) {
      case 'sum':
        return numericValues.reduce((sum, value) => sum + value, 0);
        
      case 'avg':
        const total = numericValues.reduce((sum, value) => sum + value, 0);
        return total / numericValues.length;
        
      case 'min':
        return Math.min(...numericValues);
        
      case 'max':
        return Math.max(...numericValues);
        
      default:
        return null;
    }
  }

  /**
   * Extrai valor numérico de um campo, removendo formatação se necessário
   */
  private getNumericValue(value: any): number | null {
    if (value === null || value === undefined || value === '') {
      return null;
    }

    // Se já é um número
    if (typeof value === 'number') {
      return value;
    }

    // Se é string, tentar extrair número
    if (typeof value === 'string') {
      // Remover R$ e espaços primeiro
      let cleanValue = value.replace(/[R$\s]/g, '');
      
      // Detectar formato: se tem vírgula, assumir formato brasileiro (1.000,50)
      if (cleanValue.includes(',')) {
        // Formato brasileiro: pontos são separadores de milhares, vírgula é decimal
        cleanValue = cleanValue
          .replace(/\./g, '') // Remove pontos (milhares)
          .replace(',', '.'); // Troca vírgula por ponto (decimal)
      }
      // Se não tem vírgula, manter como está (formato internacional: 27.4)
      
      const numericValue = parseFloat(cleanValue);
      return isNaN(numericValue) ? null : numericValue;
    }

    return null;
  }

  /**
   * Formata o valor do footer de acordo com a configuração
   */
  formatFooterValue(column: Column, value: any): string {
    if (value === null || value === undefined) {
      return '-';
    }

    const config = column.footer!;
    const precision = config.precision ?? 2;

    switch (config.format) {
      case 'currency':
        return new Intl.NumberFormat('pt-BR', {
          style: 'currency',
          currency: 'BRL',
          minimumFractionDigits: precision,
          maximumFractionDigits: precision
        }).format(value);

      case 'percentage':
        return new Intl.NumberFormat('pt-BR', {
          style: 'percent',
          minimumFractionDigits: precision,
          maximumFractionDigits: precision
        }).format(value / 100);

      case 'number':
        return new Intl.NumberFormat('pt-BR', {
          minimumFractionDigits: precision,
          maximumFractionDigits: precision
        }).format(value);

      default:
        // Para count, mostrar como inteiro
        if (config.type === 'count') {
          return value.toString();
        }
        // Para outros, com precisão configurada
        return Number(value).toFixed(precision);
    }
  }

  /**
   * Retorna o label do footer (se configurado) ou um padrão baseado no tipo
   */
  getFooterLabel(column: Column): string {
    if (!column.footer) {
      return '';
    }

    if (column.footer.label) {
      return column.footer.label;
    }

    // Labels padrão baseados no tipo
    switch (column.footer.type) {
      case 'sum': return 'Total:';
      case 'avg': return 'Média:';
      case 'count': return 'Itens:';
      case 'min': return 'Mínimo:';
      case 'max': return 'Máximo:';
      default: return '';
    }
  }

  /**
   * Verifica se deve mostrar o footer
   */
  get shouldShowFooter(): boolean {
    return this.showFooter && Object.keys(this.footerValues).length > 0;
  }

}
