import {
  ChangeDetectionStrategy,
  Component,
  computed,
  effect,
  input,
  output,
  signal,
  untracked
} from '@angular/core';
import { CommonModule } from '@angular/common';
import {
  ProductAttribute,
  ProductAttributeOption,
  ProductVariant,
  ProductVariantMap
} from '../../../../core/services/catalog/catalog.interface';

@Component({
  selector: 'app-product-attribute-selector',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './product-attribute-selector.component.html',
  styleUrl: './product-attribute-selector.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProductAttributeSelectorComponent {
  public attributes = input<ProductAttribute[]>([]);
  public variantsMap = input<ProductVariantMap[]>([]);
  public defaultVariant = input<ProductVariant | null>(null);

  public variantChange = output<ProductVariantMap | null>();

  protected readonly selectedAttributeOptions = signal<Record<number, number>>({});

  protected readonly availableOptions = computed(() => {
    const selections = this.selectedAttributeOptions();
    const variants = this.variantsMap();
    const attributes = this.attributes();

    const availability: Record<number, Record<number, boolean>> = {};

    for (const attribute of attributes) {
      availability[attribute.id] = {};
      for (const option of attribute.options) {
        const optionNormalized = this.normalizeText(option.value || option.label);
        
        const isAvailable = variants.some(variant => {
          if (variant.cantidad_maxima <= 0) return false;

          const variantAttrValue = this.getDefaultVariantAttributeValue(attribute, variant.attributes);
          if (!variantAttrValue || this.normalizeText(variantAttrValue) !== optionNormalized) {
             return false;
          }

          for (const otherAttr of attributes) {
             if (otherAttr.id === attribute.id) continue;
             const selectedOptionId = selections[otherAttr.id];
             if (selectedOptionId !== undefined) {
                const selectedOption = otherAttr.options.find(o => o.id === selectedOptionId);
                if (selectedOption) {
                   const selectedNormalized = this.normalizeText(selectedOption.value || selectedOption.label);
                   const vAttrValue = this.getDefaultVariantAttributeValue(otherAttr, variant.attributes);
                   if (!vAttrValue || this.normalizeText(vAttrValue) !== selectedNormalized) {
                      return false;
                   }
                }
             }
          }
          return true;
        });

        availability[attribute.id][option.id] = isAvailable;
      }
    }

    return availability;
  });

  public reset(): void {
    this.initializeSelections(this.attributes(), this.defaultVariant());
  }

  constructor() {
    effect(() => {
      const attributes = this.attributes();
      const defaultVariant = this.defaultVariant();
      
      untracked(() => {
        this.initializeSelections(attributes, defaultVariant);
      });
    });

    effect(() => {
      const selections = this.selectedAttributeOptions();
      const variantsMap = this.variantsMap();
      const attributes = this.attributes();
      
      untracked(() => {
         this.emitMatchingVariant(selections, variantsMap, attributes);
      });
    });
  }

  protected hasSelectedOption(attribute: ProductAttribute, option: ProductAttributeOption): boolean {
    return this.selectedAttributeOptions()[attribute.id] === option.id;
  }

  protected selectAttributeOption(
    attribute: ProductAttribute,
    option: ProductAttributeOption
  ): void {
    this.selectedAttributeOptions.update((current) => ({
      ...current,
      [attribute.id]: option.id
    }));
  }

  protected isColorOption(option: ProductAttributeOption): boolean {
    return this.findFirstHexValue(option.metadata) !== null;
  }

  protected getOptionSwatchColor(option: ProductAttributeOption): string {
    return this.findFirstHexValue(option.metadata) ?? '#D9D9D9';
  }

  private initializeSelections(attributes: ProductAttribute[], defaultVariant: ProductVariant | null): void {
    const selections: Record<number, number> = {};

    for (const attribute of attributes) {
      const optionId = this.findDefaultOptionId(attribute, defaultVariant);
      if (optionId !== null) {
        selections[attribute.id] = optionId;
      }
    }

    this.selectedAttributeOptions.set(selections);
  }

  private findDefaultOptionId(
    attribute: ProductAttribute,
    variant: ProductVariant | null
  ): number | null {
    if (!variant) {
      return null;
    }

    const defaultValue = this.getDefaultVariantAttributeValue(attribute, variant.definitions);
    if (!defaultValue) {
      return null;
    }

    const normalizedValue = this.normalizeText(defaultValue);
    const matchByValue = attribute.options.find(
      (option) => this.normalizeText(option.value) === normalizedValue
    );

    if (matchByValue) {
      return matchByValue.id;
    }

    const matchByLabel = attribute.options.find(
      (option) => this.normalizeText(option.label) === normalizedValue
    );

    return matchByLabel?.id ?? null;
  }

  private getDefaultVariantAttributeValue(
    attribute: ProductAttribute,
    variantAttributes: Record<string, string>
  ): string | null {
    const normalizedCodigo = this.normalizeText(attribute.codigo);
    const normalizedNombre = this.normalizeText(attribute.nombre);

    for (const [key, value] of Object.entries(variantAttributes)) {
      const normalizedKey = this.normalizeText(key);

      if (normalizedKey === normalizedCodigo || normalizedKey === normalizedNombre) {
        return value;
      }
    }

    return null;
  }

  private normalizeText(value: string | null | undefined): string {
    return (value ?? '')
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '')
      .trim()
      .toLowerCase();
  }

  private findFirstHexValue(value: unknown): string | null {
    if (typeof value === 'string') {
      const match = value.match(/#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b/);
      return match ? match[0] : null;
    }

    if (Array.isArray(value)) {
      for (const item of value) {
        const found = this.findFirstHexValue(item);
        if (found) {
          return found;
        }
      }
      return null;
    }

    if (value && typeof value === 'object') {
      for (const item of Object.values(value)) {
        const found = this.findFirstHexValue(item);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }

  private emitMatchingVariant(
    selections: Record<number, number>,
    variantsMap: ProductVariantMap[],
    attributes: ProductAttribute[]
  ): void {
    if (attributes.length === 0 || variantsMap.length === 0) {
      this.variantChange.emit(null);
      return;
    }

    const selectedValuesById: Record<number, string> = {};
    let allSelected = true;
    for (const attr of attributes) {
      const selectedOptionId = selections[attr.id];
      if (selectedOptionId === undefined) {
         allSelected = false;
         break;
      }
      const option = attr.options.find(o => o.id === selectedOptionId);
      if (option) {
         selectedValuesById[attr.id] = this.normalizeText(option.value || option.label);
      }
    }

    if (!allSelected) {
      this.variantChange.emit(null);
      return;
    }

    const matchingVariant = variantsMap.find(vMap => {
      return attributes.every(attr => {
         const selectedValue = selectedValuesById[attr.id];
         const vMapValue = this.getDefaultVariantAttributeValue(attr, vMap.attributes);
         if (!vMapValue) return false;
         return this.normalizeText(vMapValue) === selectedValue;
      });
    });

    this.variantChange.emit(matchingVariant || null);
  }
}
