/**
 * Composant de panneau de filtres avancés
 */

import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import {
  FilterGroup,
  FilterCondition,
  FieldDefinition,
  FilterOperator,
  LogicalOperator,
  FieldType
} from '../../models/filter.models';
import { FilterService } from '../../services/filter.service';
import { OPERATOR_LABELS, OPERATORS_WITHOUT_VALUE, OPERATORS_WITH_TWO_VALUES } from '../../config/filter-operators.config';

@Component({
  selector: 'app-filter-panel',
  templateUrl: './filter-panel.component.html',
  styleUrls: ['./filter-panel.component.scss']
})
export class FilterPanelComponent implements OnInit {
  @Input() fields: FieldDefinition[] = [];
  @Input() visible: boolean = false;
  @Input() initialConstraints: any = null; // Contraintes initiales depuis localStorage
  @Output() visibleChange = new EventEmitter<boolean>();
  @Output() filtersApplied = new EventEmitter<FilterGroup>();
  @Output() filtersCleared = new EventEmitter<void>();

  filterGroup: FilterGroup;
  operatorLabels = OPERATOR_LABELS;
  logicalOperatorEnum = LogicalOperator;
  fieldTypeEnum = FieldType;

  constructor(private filterService: FilterService) {
    this.filterGroup = this.filterService.createEmptyGroup();
  }

  ngOnInit(): void {
    // Charger les contraintes initiales si fournies
    if (this.initialConstraints && Object.keys(this.initialConstraints).length > 0) {
      this.loadConstraintsIntoFilterGroup(this.initialConstraints);
    } else {
      // Charger les filtres existants s'il y en a
      const existingFilters = this.filterService.getFilters();
      if (existingFilters) {
        this.filterGroup = JSON.parse(JSON.stringify(existingFilters));
      } else {
        // Ajouter une condition par défaut
        this.addCondition();
      }
    }
  }

  /**
   * Charge les contraintes API dans le FilterGroup
   */
  private loadConstraintsIntoFilterGroup(constraints: any): void {
    this.filterGroup = this.filterService.createEmptyGroup();

    // Les contraintes sont déjà extraites de constraints.and par le parent
    // On reçoit directement un objet plat { field: value }
    Object.keys(constraints).forEach(fieldName => {
      const value = constraints[fieldName];

      if (value != null && value !== '') {
        // Extraire l'opérateur si le format est "operator:value"
        let operator = FilterOperator.CONTAINS;
        let cleanValue = value;

        if (typeof value === 'string' && value.includes(':')) {
          const parts = value.split(':');
          const operatorStr = parts[0];
          cleanValue = parts.slice(1).join(':');

          // Mapper les opérateurs string vers FilterOperator
          switch (operatorStr) {
            case 'contains': operator = FilterOperator.CONTAINS; break;
            case 'equals': operator = FilterOperator.EQUALS; break;
            case 'startsWith': operator = FilterOperator.STARTS_WITH; break;
            case 'endsWith': operator = FilterOperator.ENDS_WITH; break;
            default: operator = FilterOperator.CONTAINS;
          }
        }

        const condition: FilterCondition = {
          id: `condition_${Date.now()}_${Math.random()}`,
          field: fieldName,
          operator: operator,
          value: cleanValue,
          value2: null
        };

        this.filterGroup.conditions.push(condition);
      }
    });

    // Si aucune condition n'a été ajoutée, ajouter une condition vide
    if (this.filterGroup.conditions.length === 0) {
      this.addCondition();
    }

    console.log('📋 Contraintes chargées dans le panneau:', this.filterGroup);
  }

  /**
   * Ajoute une nouvelle condition au groupe principal
   */
  addCondition(): void {
    const newCondition = this.filterService.createEmptyCondition();
    this.filterGroup.conditions.push(newCondition);
  }

  /**
   * Ajoute un nouveau groupe de conditions
   */
  addGroup(): void {
    const newGroup = this.filterService.createEmptyGroup(LogicalOperator.AND);
    // Ajouter une condition vide dans le nouveau groupe
    newGroup.conditions.push(this.filterService.createEmptyCondition());
    this.filterGroup.conditions.push(newGroup);
  }

  /**
   * Supprime une condition
   */
  removeCondition(index: number): void {
    this.filterGroup.conditions.splice(index, 1);

    // Si on n'a plus de conditions, ajouter une condition vide
    if (this.filterGroup.conditions.length === 0) {
      this.addCondition();
    }
  }

  /**
   * Récupère les opérateurs disponibles pour un champ
   */
  getOperatorsForField(fieldName: string): { value: FilterOperator; label: string }[] {
    const field = this.fields.find(f => f.name === fieldName);
    if (!field) return [];

    return field.operators.map(op => ({
      value: op,
      label: this.operatorLabels[op]
    }));
  }

  /**
   * Récupère le type d'un champ
   */
  getFieldType(fieldName: string): FieldType | null {
    const field = this.fields.find(f => f.name === fieldName);
    return field ? field.type : null;
  }

  /**
   * Récupère les options d'un champ select
   */
  getFieldOptions(fieldName: string): { label: string; value: any }[] {
    const field = this.fields.find(f => f.name === fieldName);
    return field?.options || [];
  }

  /**
   * Vérifie si un opérateur nécessite une valeur
   */
  operatorNeedsValue(operator: FilterOperator): boolean {
    return !OPERATORS_WITHOUT_VALUE.includes(operator);
  }

  /**
   * Vérifie si un opérateur nécessite 2 valeurs
   */
  operatorNeedsTwoValues(operator: FilterOperator): boolean {
    return OPERATORS_WITH_TWO_VALUES.includes(operator);
  }

  /**
   * Gère le changement de champ
   */
  onFieldChange(condition: FilterCondition): void {
    // Réinitialiser l'opérateur et la valeur quand on change de champ
    condition.operator = null as any;
    condition.value = null;
    condition.value2 = null;
  }

  /**
   * Gère le changement d'opérateur
   */
  onOperatorChange(condition: FilterCondition): void {
    // Réinitialiser les valeurs quand on change d'opérateur
    if (!this.operatorNeedsValue(condition.operator)) {
      condition.value = null;
      condition.value2 = null;
    } else if (!this.operatorNeedsTwoValues(condition.operator)) {
      condition.value2 = null;
    }
  }

  /**
   * Vérifie si une condition est un groupe
   */
  isGroup(item: FilterCondition | FilterGroup): item is FilterGroup {
    return 'conditions' in item && Array.isArray((item as FilterGroup).conditions);
  }

  /**
   * Applique les filtres
   */
  applyFilters(): void {
    // Valider les filtres
    if (this.filterService.isGroupValid(this.filterGroup)) {
      this.filtersApplied.emit(this.filterGroup);
      this.close();
    } else {
      alert('Veuillez remplir tous les champs de filtre correctement.');
    }
  }

  /**
   * Réinitialise tous les filtres
   */
  clearFilters(): void {
    this.filterGroup = this.filterService.createEmptyGroup();
    this.addCondition();
    this.filterService.clearFilters();
    this.filtersCleared.emit();
    this.close();
  }

  /**
   * Ferme le panneau
   */
  close(): void {
    this.visible = false;
    this.visibleChange.emit(false);
  }

  /**
   * Change l'opérateur logique du groupe principal
   */
  toggleLogicalOperator(): void {
    this.filterGroup.logicalOperator =
      this.filterGroup.logicalOperator === LogicalOperator.AND
        ? LogicalOperator.OR
        : LogicalOperator.AND;
  }
}
