import {
  Component,
  input,
  output,
  signal,
  OnInit,
  inject,
  effect,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import {
  ReactiveFormsModule,
  FormBuilder,
  FormGroup,
  Validators,
} from '@angular/forms';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { AppInputComponent } from '../../../../shared/components/app-input/app-input.component';
import { AppDropzoneComponent } from '../../../../shared/components/app-dropzone/app-dropzone.component';
import { AppButtonComponent } from '../../../../shared/components/app-button/app-button.component';
import { StlViewerComponent } from '../../../../shared/components/stl-viewer/stl-viewer.component';
import { ColorSelectorComponent } from '../../../../shared/components/color-selector/color-selector.component';
import {
  QuoteRequest,
  QuoteRequestItem,
  QuoteEstimatorService,
  OptionsResponse,
  SimpleOption,
  MaterialOption,
  VariantOption,
} from '../../services/quote-estimator.service';
import { getColorHex } from '../../../../core/constants/colors.const';

interface FormItem {
  file: File;
  previewFile?: File;
  quantity: number;
  material: string;
  quality: string;
  color: string;
  filamentVariantId?: number;
  supportEnabled: boolean;
  infillDensity: number;
  infillPattern: string;
  layerHeight: number;
  nozzleDiameter: number;
}

interface ItemSettingsDiffInfo {
  differences: string[];
}

type ItemPrintSettingsUpdate = Partial<
  Pick<
    FormItem,
    | 'material'
    | 'quality'
    | 'nozzleDiameter'
    | 'layerHeight'
    | 'infillDensity'
    | 'infillPattern'
    | 'supportEnabled'
  >
>;

@Component({
  selector: 'app-upload-form',
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    TranslateModule,
    AppInputComponent,
    AppDropzoneComponent,
    AppButtonComponent,
    StlViewerComponent,
    ColorSelectorComponent,
  ],
  templateUrl: './upload-form.component.html',
  styleUrl: './upload-form.component.scss',
})
export class UploadFormComponent implements OnInit {
  mode = input<'easy' | 'advanced'>('easy');
  lockedSettings = input<boolean>(false);
  loading = input<boolean>(false);
  uploadProgress = input<number>(0);

  submitRequest = output<QuoteRequest>();
  itemQuantityChange = output<{
    index: number;
    fileName: string;
    quantity: number;
  }>();
  itemSettingsDiffChange = output<Record<string, ItemSettingsDiffInfo>>();
  printSettingsChange = output<{
    mode: 'easy' | 'advanced';
    material: string;
    quality: string;
    nozzleDiameter: number;
    layerHeight: number;
    infillDensity: number;
    infillPattern: string;
    supportEnabled: boolean;
  }>();

  private estimator = inject(QuoteEstimatorService);
  private fb = inject(FormBuilder);
  private translate = inject(TranslateService);

  form: FormGroup;

  items = signal<FormItem[]>([]);
  selectedFile = signal<File | null>(null);
  sameSettingsForAll = signal(true);

  materials = signal<SimpleOption[]>([]);
  qualities = signal<SimpleOption[]>([]);
  nozzleDiameters = signal<SimpleOption[]>([]);
  infillPatterns = signal<SimpleOption[]>([]);
  layerHeights = signal<SimpleOption[]>([]);
  currentMaterialVariants = signal<VariantOption[]>([]);

  private fullMaterialOptions: MaterialOption[] = [];
  private allLayerHeights: SimpleOption[] = [];
  private layerHeightsByNozzle: Record<string, SimpleOption[]> = {};
  private isPatchingSettings = false;

  acceptedFormats = '.stl,.3mf,.step,.stp';

  constructor() {
    this.form = this.fb.group({
      itemsTouched: [false],
      syncAllItems: [true],
      material: ['', Validators.required],
      quality: ['standard', Validators.required],
      notes: [''],
      infillDensity: [15, [Validators.min(0), Validators.max(100)]],
      layerHeight: [0.2, [Validators.min(0.05), Validators.max(1.0)]],
      nozzleDiameter: [0.4, Validators.required],
      infillPattern: ['grid', Validators.required],
      supportEnabled: [true],
    });

    this.form.get('material')?.valueChanges.subscribe((value) => {
      this.updateVariants(String(value || ''));
    });

    this.form.get('quality')?.valueChanges.subscribe((quality) => {
      if (this.isPatchingSettings || this.mode() !== 'easy') {
        return;
      }
      this.applyEasyPresetFromQuality(String(quality || 'standard'));
    });

    this.form.get('nozzleDiameter')?.valueChanges.subscribe((nozzle) => {
      if (this.isPatchingSettings) {
        return;
      }
      this.updateLayerHeightOptionsForNozzle(nozzle, true);
    });

    this.form.valueChanges.subscribe(() => {
      if (this.isPatchingSettings) {
        return;
      }

      if (this.sameSettingsForAll()) {
        this.applyGlobalSettingsToAllItems();
      } else {
        this.syncSelectedItemSettingsFromForm();
      }

      this.emitPrintSettingsChange();
      this.emitItemSettingsDiffChange();
    });

    effect(() => {
      this.applySettingsLock(this.lockedSettings());
    });

    effect(() => {
      if (this.mode() !== 'easy' || this.sameSettingsForAll()) {
        return;
      }

      this.sameSettingsForAll.set(true);
      this.form.get('syncAllItems')?.setValue(true, { emitEvent: false });
      this.applyGlobalSettingsToAllItems();
      this.emitPrintSettingsChange();
      this.emitItemSettingsDiffChange();
    });

    effect(() => {
      if (this.mode() !== 'advanced') {
        return;
      }

      if (this.items().length > 0 || this.sameSettingsForAll()) {
        return;
      }

      this.sameSettingsForAll.set(true);
      this.form.get('syncAllItems')?.setValue(true, { emitEvent: false });
    });
  }

  ngOnInit() {
    this.estimator.getOptions().subscribe({
      next: (options: OptionsResponse) => {
        this.fullMaterialOptions = options.materials || [];

        this.materials.set(
          (options.materials || []).map((m) => ({
            label: m.label,
            value: m.code,
          })),
        );
        this.qualities.set(
          (options.qualities || []).map((q) => ({
            label: q.label,
            value: q.id,
          })),
        );
        this.infillPatterns.set(
          (options.infillPatterns || []).map((p) => ({
            label: p.label,
            value: p.id,
          })),
        );
        this.nozzleDiameters.set(
          (options.nozzleDiameters || []).map((n) => ({
            label: n.label,
            value: n.value,
          })),
        );

        this.allLayerHeights = (options.layerHeights || []).map((l) => ({
          label: l.label,
          value: l.value,
        }));

        this.layerHeightsByNozzle = {};
        (options.layerHeightsByNozzle || []).forEach((entry) => {
          this.layerHeightsByNozzle[this.toNozzleKey(entry.nozzleDiameter)] = (
            entry.layerHeights || []
          ).map((layer) => ({
            label: layer.label,
            value: layer.value,
          }));
        });

        this.setDefaults();
      },
      error: (err) => {
        console.error('Failed to load options', err);
        this.materials.set([
          {
            label: this.translate.instant('CALC.FALLBACK_MATERIAL'),
            value: 'PLA',
          },
        ]);
        this.qualities.set([
          {
            label: this.translate.instant('CALC.FALLBACK_QUALITY_STANDARD'),
            value: 'standard',
          },
        ]);
        this.infillPatterns.set([{ label: 'Grid', value: 'grid' }]);
        this.nozzleDiameters.set([{ label: '0.4 mm', value: 0.4 }]);

        this.allLayerHeights = [{ label: '0.20 mm', value: 0.2 }];
        this.layerHeightsByNozzle = {
          [this.toNozzleKey(0.4)]: this.allLayerHeights,
        };

        this.setDefaults();
      },
    });
  }

  isStlFile(file: File | null): boolean {
    if (!file) return false;
    const name = file.name.toLowerCase();
    return name.endsWith('.stl');
  }

  canPreviewSelectedFile(): boolean {
    return this.isStlFile(this.getSelectedPreviewFile());
  }

  getSelectedPreviewFile(): File | null {
    const selected = this.selectedFile();
    if (!selected) return null;
    const item = this.items().find((i) => i.file === selected);
    return item ? item.previewFile || item.file : null;
  }

  getSelectedItemIndex(): number {
    const selected = this.selectedFile();
    if (!selected) return -1;
    return this.items().findIndex((item) => item.file === selected);
  }

  getSelectedItem(): FormItem | null {
    const index = this.getSelectedItemIndex();
    if (index < 0) return null;
    return this.items()[index] || null;
  }

  getVariantsForMaterial(
    materialCode: string | null | undefined,
  ): VariantOption[] {
    const normalized = String(materialCode || '')
      .trim()
      .toUpperCase();
    if (!normalized) return [];

    const found = this.fullMaterialOptions.find(
      (m) =>
        String(m.code || '')
          .trim()
          .toUpperCase() === normalized,
    );
    return found?.variants || [];
  }

  getLayerHeightOptionsForNozzle(nozzleRaw: unknown): SimpleOption[] {
    const key = this.toNozzleKey(nozzleRaw);
    const perNozzle = this.layerHeightsByNozzle[key];
    if (perNozzle && perNozzle.length > 0) {
      return perNozzle;
    }
    return this.allLayerHeights.length > 0
      ? this.allLayerHeights
      : [{ label: '0.20 mm', value: 0.2 }];
  }

  onFilesDropped(newFiles: File[]) {
    const MAX_SIZE = 200 * 1024 * 1024;
    const validItems: FormItem[] = [];
    let hasError = false;

    const defaults = this.getCurrentGlobalItemDefaults();

    for (const file of newFiles) {
      if (file.size > MAX_SIZE) {
        hasError = true;
        continue;
      }

      const selection = this.getDefaultVariantSelection(defaults.material);
      validItems.push({
        file,
        previewFile: this.isStlFile(file) ? file : undefined,
        quantity: 1,
        material: defaults.material,
        quality: defaults.quality,
        color: selection.colorName,
        filamentVariantId: selection.filamentVariantId,
        supportEnabled: defaults.supportEnabled,
        infillDensity: defaults.infillDensity,
        infillPattern: defaults.infillPattern,
        layerHeight: defaults.layerHeight,
        nozzleDiameter: defaults.nozzleDiameter,
      });
    }

    if (hasError) {
      alert(this.translate.instant('CALC.ERR_FILE_TOO_LARGE'));
    }

    if (validItems.length === 0) {
      return;
    }

    this.items.update((current) => [...current, ...validItems]);
    this.form.get('itemsTouched')?.setValue(true);

    if (this.sameSettingsForAll()) {
      this.applyGlobalSettingsToAllItems();
    }

    this.selectFile(validItems[validItems.length - 1].file);
    this.emitItemSettingsDiffChange();
  }

  onAdditionalFilesSelected(event: Event) {
    const input = event.target as HTMLInputElement;
    if (!input.files || input.files.length === 0) {
      return;
    }

    this.onFilesDropped(Array.from(input.files));
    input.value = '';
  }

  updateItemQuantity(index: number, event: Event) {
    const input = event.target as HTMLInputElement;
    const parsed = parseInt(input.value, 10);
    const quantity = Number.isFinite(parsed) ? parsed : 1;

    const currentItem = this.items()[index];
    if (!currentItem) {
      return;
    }

    const normalizedQty = this.normalizeQuantity(quantity);
    this.updateItemQuantityByIndex(index, quantity);

    this.itemQuantityChange.emit({
      index,
      fileName: currentItem.file.name,
      quantity: normalizedQty,
    });
  }

  updateItemQuantityByIndex(index: number, quantity: number) {
    if (!Number.isInteger(index) || index < 0) return;
    const normalizedQty = this.normalizeQuantity(quantity);

    this.items.update((current) => {
      if (index >= current.length) return current;

      return current.map((item, idx) =>
        idx === index ? { ...item, quantity: normalizedQty } : item,
      );
    });
  }

  updateItemQuantityByName(fileName: string, quantity: number) {
    const targetName = this.normalizeFileName(fileName);
    const normalizedQty = this.normalizeQuantity(quantity);

    this.items.update((current) => {
      let matched = false;

      return current.map((item) => {
        if (!matched && this.normalizeFileName(item.file.name) === targetName) {
          matched = true;
          return { ...item, quantity: normalizedQty };
        }

        return item;
      });
    });
  }

  selectFile(file: File) {
    if (this.selectedFile() !== file) {
      this.selectedFile.set(file);
    }
    this.loadSelectedItemSettingsIntoForm();
  }

  getSelectedFileColor(): string {
    const selected = this.selectedFile();
    if (!selected) {
      return '#facf0a';
    }

    const item = this.items().find((i) => i.file === selected);
    if (!item) {
      return '#facf0a';
    }

    const variants = this.getVariantsForMaterial(item.material);
    if (variants.length > 0) {
      const byId =
        item.filamentVariantId != null
          ? variants.find((v) => v.id === item.filamentVariantId)
          : null;
      const byColor = variants.find((v) => v.colorName === item.color);
      const selectedVariant = byId || byColor;
      if (selectedVariant) {
        return selectedVariant.hexColor;
      }
    }

    return getColorHex(item.color);
  }

  updateItemColor(
    index: number,
    newSelection: string | { colorName: string; filamentVariantId?: number },
  ) {
    const colorName =
      typeof newSelection === 'string' ? newSelection : newSelection.colorName;
    const filamentVariantId =
      typeof newSelection === 'string'
        ? undefined
        : newSelection.filamentVariantId;

    this.items.update((current) => {
      if (index < 0 || index >= current.length) {
        return current;
      }

      return current.map((item, idx) =>
        idx === index
          ? {
              ...item,
              color: colorName,
              filamentVariantId,
            }
          : item,
      );
    });
  }

  removeItem(index: number) {
    let nextSelected: File | null = null;

    this.items.update((current) => {
      const updated = [...current];
      const removed = updated.splice(index, 1)[0];
      if (!removed) {
        return current;
      }

      if (this.selectedFile() === removed.file) {
        nextSelected =
          updated.length > 0 ? updated[Math.max(0, index - 1)].file : null;
      }

      return updated;
    });

    if (nextSelected) {
      this.selectFile(nextSelected);
    } else if (this.items().length === 0) {
      this.selectedFile.set(null);
    }

    this.emitItemSettingsDiffChange();
  }

  onSameSettingsToggle(enabled: boolean) {
    this.sameSettingsForAll.set(enabled);
    this.form.get('syncAllItems')?.setValue(enabled, { emitEvent: false });

    if (enabled) {
      this.applyGlobalSettingsToAllItems();
    } else {
      this.loadSelectedItemSettingsIntoForm();
    }

    this.emitPrintSettingsChange();
    this.emitItemSettingsDiffChange();
  }

  patchSettings(settings: any) {
    if (!settings) return;

    const patch: any = {};
    if (settings.materialCode) patch.material = settings.materialCode;

    const layer = Number(settings.layerHeightMm);
    if (Number.isFinite(layer)) {
      patch.layerHeight = layer;
      patch.quality =
        layer >= 0.24 ? 'draft' : layer <= 0.12 ? 'extra_fine' : 'standard';
    }

    const nozzle = Number(settings.nozzleDiameterMm);
    if (Number.isFinite(nozzle)) patch.nozzleDiameter = nozzle;

    const infill = Number(settings.infillPercent);
    if (Number.isFinite(infill)) patch.infillDensity = infill;

    if (settings.infillPattern) patch.infillPattern = settings.infillPattern;
    if (settings.supportsEnabled !== undefined)
      patch.supportEnabled = Boolean(settings.supportsEnabled);
    if (settings.notes) patch.notes = settings.notes;

    this.isPatchingSettings = true;
    this.form.patchValue(patch, { emitEvent: false });
    this.isPatchingSettings = false;

    this.updateVariants(String(this.form.get('material')?.value || ''));
    this.updateLayerHeightOptionsForNozzle(
      this.form.get('nozzleDiameter')?.value,
      true,
    );

    if (this.sameSettingsForAll()) {
      this.applyGlobalSettingsToAllItems();
    } else {
      this.syncSelectedItemSettingsFromForm();
    }

    this.emitPrintSettingsChange();
    this.emitItemSettingsDiffChange();
  }

  setFiles(files: File[]) {
    const defaults = this.getCurrentGlobalItemDefaults();
    const selection = this.getDefaultVariantSelection(defaults.material);

    const validItems: FormItem[] = files.map((file) => ({
      file,
      previewFile: this.isStlFile(file) ? file : undefined,
      quantity: 1,
      material: defaults.material,
      quality: defaults.quality,
      color: selection.colorName,
      filamentVariantId: selection.filamentVariantId,
      supportEnabled: defaults.supportEnabled,
      infillDensity: defaults.infillDensity,
      infillPattern: defaults.infillPattern,
      layerHeight: defaults.layerHeight,
      nozzleDiameter: defaults.nozzleDiameter,
    }));

    this.items.set(validItems);

    if (validItems.length > 0) {
      this.form.get('itemsTouched')?.setValue(true);
      this.selectFile(validItems[validItems.length - 1].file);
    } else {
      this.selectedFile.set(null);
    }

    this.emitItemSettingsDiffChange();
  }

  setPreviewFileByIndex(index: number, previewFile: File) {
    if (!Number.isInteger(index) || index < 0) return;

    this.items.update((current) => {
      if (index >= current.length) return current;
      return current.map((item, idx) =>
        idx === index ? { ...item, previewFile } : item,
      );
    });
  }

  setItemPrintSettingsByIndex(index: number, update: ItemPrintSettingsUpdate) {
    if (!Number.isInteger(index) || index < 0) return;

    this.items.update((current) => {
      if (index >= current.length) return current;

      return current.map((item, idx) => {
        if (idx !== index) {
          return item;
        }

        let next: FormItem = {
          ...item,
          ...update,
        };

        if (update.quality !== undefined) {
          next.quality = this.normalizeQualityValue(update.quality);
        }

        if (update.material !== undefined) {
          const variants = this.getVariantsForMaterial(update.material);
          const byId =
            next.filamentVariantId != null
              ? variants.find((v) => v.id === next.filamentVariantId)
              : null;
          const byColor = variants.find((v) => v.colorName === next.color);
          const fallback = variants.find((v) => !v.isOutOfStock) || variants[0];
          const variant = byId || byColor || fallback;
          if (variant) {
            next = {
              ...next,
              color: variant.colorName,
              filamentVariantId: variant.id,
            };
          }
        }

        return next;
      });
    });

    this.refreshSameSettingsFlag();

    if (!this.sameSettingsForAll() && this.getSelectedItemIndex() === index) {
      this.loadSelectedItemSettingsIntoForm();
      this.emitPrintSettingsChange();
    }

    this.emitItemSettingsDiffChange();
  }

  getCurrentRequestDraft(): QuoteRequest {
    const defaults = this.getCurrentGlobalItemDefaults();

    const items: QuoteRequestItem[] = this.items().map((item) =>
      this.toRequestItem(item, defaults),
    );

    return {
      items,
      material: defaults.material,
      quality: defaults.quality,
      notes: this.form.get('notes')?.value || '',
      infillDensity: defaults.infillDensity,
      infillPattern: defaults.infillPattern,
      supportEnabled: defaults.supportEnabled,
      layerHeight: defaults.layerHeight,
      nozzleDiameter: defaults.nozzleDiameter,
      mode: this.mode(),
    };
  }

  onSubmit() {
    if (!this.form.valid || this.items().length === 0) {
      this.form.markAllAsTouched();
      this.form.get('itemsTouched')?.setValue(true);
      return;
    }

    this.submitRequest.emit(this.getCurrentRequestDraft());
  }

  private setDefaults() {
    if (this.materials().length > 0 && !this.form.get('material')?.value) {
      const exactPla = this.materials().find(
        (m) => typeof m.value === 'string' && m.value.toUpperCase() === 'PLA',
      );
      const fallback = exactPla || this.materials()[0];
      this.form.get('material')?.setValue(fallback.value, { emitEvent: false });
    }

    if (this.qualities().length > 0 && !this.form.get('quality')?.value) {
      const standard = this.qualities().find((q) => q.value === 'standard');
      this.form
        .get('quality')
        ?.setValue(standard ? standard.value : this.qualities()[0].value, {
          emitEvent: false,
        });
    }

    if (
      this.nozzleDiameters().length > 0 &&
      !this.form.get('nozzleDiameter')?.value
    ) {
      this.form.get('nozzleDiameter')?.setValue(0.4, { emitEvent: false });
    }

    if (
      this.infillPatterns().length > 0 &&
      !this.form.get('infillPattern')?.value
    ) {
      this.form
        .get('infillPattern')
        ?.setValue(this.infillPatterns()[0].value, { emitEvent: false });
    }

    this.updateVariants(String(this.form.get('material')?.value || ''));
    this.updateLayerHeightOptionsForNozzle(
      this.form.get('nozzleDiameter')?.value,
      true,
    );

    if (this.mode() === 'easy') {
      this.applyEasyPresetFromQuality(
        String(this.form.get('quality')?.value || 'standard'),
      );
    }

    this.emitPrintSettingsChange();
  }

  private applyEasyPresetFromQuality(qualityRaw: string) {
    const preset = this.easyModePresetForQuality(qualityRaw);

    this.isPatchingSettings = true;
    this.form.patchValue(
      {
        quality: preset.quality,
        nozzleDiameter: preset.nozzleDiameter,
        layerHeight: preset.layerHeight,
        infillDensity: preset.infillDensity,
        infillPattern: preset.infillPattern,
      },
      { emitEvent: false },
    );
    this.isPatchingSettings = false;

    this.updateLayerHeightOptionsForNozzle(preset.nozzleDiameter, true);
  }

  private easyModePresetForQuality(qualityRaw: string): {
    quality: string;
    nozzleDiameter: number;
    layerHeight: number;
    infillDensity: number;
    infillPattern: string;
  } {
    const quality = this.normalizeQualityValue(qualityRaw);

    if (quality === 'draft') {
      return {
        quality: 'draft',
        nozzleDiameter: 0.4,
        layerHeight: 0.28,
        infillDensity: 15,
        infillPattern: 'grid',
      };
    }

    if (quality === 'extra_fine') {
      return {
        quality: 'extra_fine',
        nozzleDiameter: 0.4,
        layerHeight: 0.12,
        infillDensity: 20,
        infillPattern: 'gyroid',
      };
    }

    return {
      quality: 'standard',
      nozzleDiameter: 0.4,
      layerHeight: 0.2,
      infillDensity: 15,
      infillPattern: 'grid',
    };
  }

  private getCurrentGlobalItemDefaults(): {
    material: string;
    quality: string;
    nozzleDiameter: number;
    layerHeight: number;
    infillDensity: number;
    infillPattern: string;
    supportEnabled: boolean;
  } {
    const material = String(this.form.get('material')?.value || 'PLA');
    const quality = this.normalizeQualityValue(this.form.get('quality')?.value);

    if (this.mode() === 'easy') {
      const preset = this.easyModePresetForQuality(quality);
      return {
        material,
        quality: preset.quality,
        nozzleDiameter: preset.nozzleDiameter,
        layerHeight: preset.layerHeight,
        infillDensity: preset.infillDensity,
        infillPattern: preset.infillPattern,
        supportEnabled: Boolean(this.form.get('supportEnabled')?.value),
      };
    }

    return {
      material,
      quality,
      nozzleDiameter: this.normalizeNumber(
        this.form.get('nozzleDiameter')?.value,
        0.4,
      ),
      layerHeight: this.normalizeNumber(
        this.form.get('layerHeight')?.value,
        0.2,
      ),
      infillDensity: this.normalizeNumber(
        this.form.get('infillDensity')?.value,
        20,
      ),
      infillPattern: String(this.form.get('infillPattern')?.value || 'grid'),
      supportEnabled: Boolean(this.form.get('supportEnabled')?.value),
    };
  }

  private toRequestItem(
    item: FormItem,
    defaults: ReturnType<UploadFormComponent['getCurrentGlobalItemDefaults']>,
  ): QuoteRequestItem {
    const quality = this.normalizeQualityValue(
      item.quality || defaults.quality,
    );

    if (this.mode() === 'easy') {
      const preset = this.easyModePresetForQuality(quality);
      return {
        file: item.file,
        quantity: this.normalizeQuantity(item.quantity),
        material: item.material || defaults.material,
        quality: preset.quality,
        color: item.color,
        filamentVariantId: item.filamentVariantId,
        supportEnabled: item.supportEnabled ?? defaults.supportEnabled,
        infillDensity: preset.infillDensity,
        infillPattern: preset.infillPattern,
        layerHeight: preset.layerHeight,
        nozzleDiameter: preset.nozzleDiameter,
      };
    }

    return {
      file: item.file,
      quantity: this.normalizeQuantity(item.quantity),
      material: item.material || defaults.material,
      quality,
      color: item.color,
      filamentVariantId: item.filamentVariantId,
      supportEnabled: item.supportEnabled,
      infillDensity: this.normalizeNumber(
        item.infillDensity,
        defaults.infillDensity,
      ),
      infillPattern: item.infillPattern || defaults.infillPattern,
      layerHeight: this.normalizeNumber(item.layerHeight, defaults.layerHeight),
      nozzleDiameter: this.normalizeNumber(
        item.nozzleDiameter,
        defaults.nozzleDiameter,
      ),
    };
  }

  private applyGlobalSettingsToAllItems() {
    const defaults = this.getCurrentGlobalItemDefaults();
    const variants = this.getVariantsForMaterial(defaults.material);
    const fallback = variants.find((v) => !v.isOutOfStock) || variants[0];

    this.items.update((current) =>
      current.map((item) => {
        const byId =
          item.filamentVariantId != null
            ? variants.find((v) => v.id === item.filamentVariantId)
            : null;
        const byColor = variants.find((v) => v.colorName === item.color);
        const selectedVariant = byId || byColor || fallback;

        return {
          ...item,
          material: defaults.material,
          quality: defaults.quality,
          nozzleDiameter: defaults.nozzleDiameter,
          layerHeight: defaults.layerHeight,
          infillDensity: defaults.infillDensity,
          infillPattern: defaults.infillPattern,
          supportEnabled: defaults.supportEnabled,
          color: selectedVariant ? selectedVariant.colorName : item.color,
          filamentVariantId: selectedVariant
            ? selectedVariant.id
            : item.filamentVariantId,
        };
      }),
    );
  }

  private syncSelectedItemSettingsFromForm() {
    if (this.sameSettingsForAll()) {
      return;
    }

    const index = this.getSelectedItemIndex();
    if (index < 0) {
      return;
    }

    const defaults = this.getCurrentGlobalItemDefaults();

    this.items.update((current) => {
      if (index >= current.length) return current;

      return current.map((item, idx) => {
        if (idx !== index) {
          return item;
        }

        const variants = this.getVariantsForMaterial(defaults.material);
        const byId =
          item.filamentVariantId != null
            ? variants.find((v) => v.id === item.filamentVariantId)
            : null;
        const byColor = variants.find((v) => v.colorName === item.color);
        const fallback = variants.find((v) => !v.isOutOfStock) || variants[0];
        const selectedVariant = byId || byColor || fallback;

        return {
          ...item,
          material: defaults.material,
          quality: defaults.quality,
          nozzleDiameter: defaults.nozzleDiameter,
          layerHeight: defaults.layerHeight,
          infillDensity: defaults.infillDensity,
          infillPattern: defaults.infillPattern,
          supportEnabled: defaults.supportEnabled,
          color: selectedVariant ? selectedVariant.colorName : item.color,
          filamentVariantId: selectedVariant
            ? selectedVariant.id
            : item.filamentVariantId,
        };
      });
    });
  }

  private loadSelectedItemSettingsIntoForm() {
    if (this.sameSettingsForAll()) {
      return;
    }

    const selected = this.getSelectedItem();
    if (!selected) {
      return;
    }

    this.isPatchingSettings = true;
    this.form.patchValue(
      {
        material: selected.material,
        quality: this.normalizeQualityValue(selected.quality),
        nozzleDiameter: selected.nozzleDiameter,
        layerHeight: selected.layerHeight,
        infillDensity: selected.infillDensity,
        infillPattern: selected.infillPattern,
        supportEnabled: selected.supportEnabled,
      },
      { emitEvent: false },
    );
    this.isPatchingSettings = false;

    this.updateVariants(selected.material);
    this.updateLayerHeightOptionsForNozzle(selected.nozzleDiameter, true);
  }

  private updateVariants(materialCode: string) {
    const variants = this.getVariantsForMaterial(materialCode);
    this.currentMaterialVariants.set(variants);

    if (this.sameSettingsForAll() || !this.selectedFile()) {
      return;
    }

    if (variants.length === 0) {
      return;
    }

    const selectedIndex = this.getSelectedItemIndex();
    if (selectedIndex < 0) {
      return;
    }

    this.items.update((current) => {
      if (selectedIndex >= current.length) {
        return current;
      }

      const selectedItem = current[selectedIndex];
      const byId =
        selectedItem.filamentVariantId != null
          ? variants.find((v) => v.id === selectedItem.filamentVariantId)
          : null;
      const byColor = variants.find((v) => v.colorName === selectedItem.color);
      const fallback = variants.find((v) => !v.isOutOfStock) || variants[0];
      const selectedVariant = byId || byColor || fallback;

      if (!selectedVariant) {
        return current;
      }

      return current.map((item, idx) =>
        idx === selectedIndex
          ? {
              ...item,
              color: selectedVariant.colorName,
              filamentVariantId: selectedVariant.id,
            }
          : item,
      );
    });
  }

  private updateLayerHeightOptionsForNozzle(
    nozzleRaw: unknown,
    clampCurrentLayer: boolean,
  ) {
    const options = this.getLayerHeightOptionsForNozzle(nozzleRaw);
    this.layerHeights.set(options);

    if (!clampCurrentLayer || options.length === 0) {
      return;
    }

    const currentLayer = this.normalizeNumber(
      this.form.get('layerHeight')?.value,
      options[0].value as number,
    );
    const allowed = options.some(
      (option) =>
        Math.abs(
          this.normalizeNumber(option.value, currentLayer) - currentLayer,
        ) < 0.0001,
    );

    if (allowed) {
      return;
    }

    this.isPatchingSettings = true;
    this.form.patchValue(
      {
        layerHeight: Number(options[0].value),
      },
      { emitEvent: false },
    );
    this.isPatchingSettings = false;
  }

  private emitPrintSettingsChange() {
    const defaults = this.getCurrentGlobalItemDefaults();
    this.printSettingsChange.emit({
      mode: this.mode(),
      material: defaults.material,
      quality: defaults.quality,
      nozzleDiameter: defaults.nozzleDiameter,
      layerHeight: defaults.layerHeight,
      infillDensity: defaults.infillDensity,
      infillPattern: defaults.infillPattern,
      supportEnabled: defaults.supportEnabled,
    });
  }

  private emitItemSettingsDiffChange() {
    if (this.sameSettingsForAll()) {
      this.itemSettingsDiffChange.emit({});
      return;
    }

    const baseline = this.getCurrentGlobalItemDefaults();
    const diffByFileName: Record<string, ItemSettingsDiffInfo> = {};

    this.items().forEach((item) => {
      const differences: string[] = [];

      if (
        this.normalizeText(item.material) !==
        this.normalizeText(baseline.material)
      ) {
        differences.push(item.material.toUpperCase());
      }

      if (this.mode() === 'easy') {
        if (
          this.normalizeText(item.quality) !==
          this.normalizeText(baseline.quality)
        ) {
          differences.push(`quality:${item.quality}`);
        }
      } else {
        if (
          Math.abs(
            this.normalizeNumber(item.nozzleDiameter, baseline.nozzleDiameter) -
              baseline.nozzleDiameter,
          ) > 0.0001
        ) {
          differences.push(`nozzle:${item.nozzleDiameter}`);
        }

        if (
          Math.abs(
            this.normalizeNumber(item.layerHeight, baseline.layerHeight) -
              baseline.layerHeight,
          ) > 0.0001
        ) {
          differences.push(`layer:${item.layerHeight}`);
        }

        if (
          Math.abs(
            this.normalizeNumber(item.infillDensity, baseline.infillDensity) -
              baseline.infillDensity,
          ) > 0.0001
        ) {
          differences.push(`infill:${item.infillDensity}%`);
        }

        if (
          this.normalizeText(item.infillPattern) !==
          this.normalizeText(baseline.infillPattern)
        ) {
          differences.push(`pattern:${item.infillPattern}`);
        }

        if (Boolean(item.supportEnabled) !== Boolean(baseline.supportEnabled)) {
          differences.push(
            `support:${Boolean(item.supportEnabled) ? 'on' : 'off'}`,
          );
        }
      }

      if (differences.length > 0) {
        diffByFileName[item.file.name] = { differences };
      }
    });

    this.itemSettingsDiffChange.emit(diffByFileName);
  }

  private getDefaultVariantSelection(materialCode: string): {
    colorName: string;
    filamentVariantId?: number;
  } {
    const variants = this.getVariantsForMaterial(materialCode);
    if (variants.length === 0) {
      return { colorName: 'Black' };
    }

    const preferred = variants.find((v) => !v.isOutOfStock) || variants[0];
    return {
      colorName: preferred.colorName,
      filamentVariantId: preferred.id,
    };
  }

  private refreshSameSettingsFlag() {
    const current = this.items();
    if (current.length <= 1) {
      return;
    }

    const first = current[0];
    const allEqual = current.every((item) =>
      this.sameItemSettings(first, item),
    );

    if (!allEqual) {
      this.sameSettingsForAll.set(false);
      this.form.get('syncAllItems')?.setValue(false, { emitEvent: false });
    }
  }

  private sameItemSettings(a: FormItem, b: FormItem): boolean {
    return (
      this.normalizeText(a.material) === this.normalizeText(b.material) &&
      this.normalizeText(a.quality) === this.normalizeText(b.quality) &&
      Math.abs(
        this.normalizeNumber(a.nozzleDiameter, 0.4) -
          this.normalizeNumber(b.nozzleDiameter, 0.4),
      ) < 0.0001 &&
      Math.abs(
        this.normalizeNumber(a.layerHeight, 0.2) -
          this.normalizeNumber(b.layerHeight, 0.2),
      ) < 0.0001 &&
      Math.abs(
        this.normalizeNumber(a.infillDensity, 20) -
          this.normalizeNumber(b.infillDensity, 20),
      ) < 0.0001 &&
      this.normalizeText(a.infillPattern) ===
        this.normalizeText(b.infillPattern) &&
      Boolean(a.supportEnabled) === Boolean(b.supportEnabled)
    );
  }

  private normalizeQualityValue(value: any): string {
    const normalized = String(value || 'standard')
      .trim()
      .toLowerCase();
    if (normalized === 'high' || normalized === 'high_definition') {
      return 'extra_fine';
    }
    return normalized || 'standard';
  }

  private normalizeQuantity(quantity: number): number {
    if (!Number.isFinite(quantity) || quantity < 1) {
      return 1;
    }
    return Math.floor(quantity);
  }

  private normalizeNumber(value: any, fallback: number): number {
    const numeric = Number(value);
    return Number.isFinite(numeric) ? numeric : fallback;
  }

  private normalizeText(value: any): string {
    return String(value || '')
      .trim()
      .toLowerCase();
  }

  private normalizeFileName(fileName: string): string {
    return (fileName || '').split(/[\\/]/).pop()?.trim().toLowerCase() ?? '';
  }

  private toNozzleKey(value: unknown): string {
    const numeric = Number(value);
    if (!Number.isFinite(numeric)) {
      return '0.40';
    }
    return numeric.toFixed(2);
  }

  private applySettingsLock(locked: boolean): void {
    const controlsToLock = [
      'syncAllItems',
      'material',
      'quality',
      'nozzleDiameter',
      'infillPattern',
      'layerHeight',
      'infillDensity',
      'supportEnabled',
    ];

    controlsToLock.forEach((name) => {
      const control = this.form.get(name);
      if (!control) return;

      if (locked) {
        control.disable({ emitEvent: false });
      } else {
        control.enable({ emitEvent: false });
      }
    });
  }
}
