import { Component, inject, OnInit, OnDestroy, signal, ViewChild, ElementRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, FormArray, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { Subject, firstValueFrom } from 'rxjs';
import { debounceTime, switchMap, filter, distinctUntilChanged } from 'rxjs/operators';
import { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { InputNumberModule } from 'primeng/inputnumber';
import { SelectModule } from 'primeng/select';
import { DatePickerModule } from 'primeng/datepicker';
import { TagModule } from 'primeng/tag';
import { ToastModule } from 'primeng/toast';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { TextareaModule } from 'primeng/textarea';
import { TooltipModule } from 'primeng/tooltip';
import { DialogModule } from 'primeng/dialog';
import { MessageService, ConfirmationService } from 'primeng/api';
import { PageHeaderComponent, DataTableComponent, FormDialogComponent, TableColumn } from '../../shared';
import { StockReceiptService } from '../../core/services/stock-receipt.service';
import { ProductService } from '../../core/services/product.service';
import { SettingService } from '../../core/services/setting.service';
import { StockReceipt, StockBalance } from '../../core/models/inventory.model';
import { StockBalanceService } from '../../core/services/stock-balance.service';
import { Product, ProductVariant } from '../../core/models/product.model';
import { selectAllUnits, UnitsActions, PartnersActions, selectAllPartners } from '../../core/store';
import { WarehouseService } from '../../core/services/warehouse.service';
import { Warehouse } from '../../core/models/warehouse.model';
import { Partner } from '../../core/models/partner.model';

@Component({
  selector: 'app-stock-receipts',
  standalone: true,
  imports: [
    CommonModule, FormsModule, ReactiveFormsModule, TranslateModule,
    TableModule, ButtonModule, InputTextModule, InputNumberModule,
    SelectModule, DatePickerModule, TagModule, ToastModule,
    ConfirmDialogModule, TextareaModule, TooltipModule, DialogModule,
    PageHeaderComponent, DataTableComponent, FormDialogComponent,
  ],
  providers: [MessageService, ConfirmationService],
  templateUrl: './stock-receipts.component.html',
  styleUrl: './stock-receipts.component.scss',
})
export class StockReceiptsComponent implements OnInit, OnDestroy {
  readonly columns: TableColumn[] = [
    { field: 'receipt_number', header: 'INVENTORY.RECEIPT_NUMBER' },
    { field: 'date', header: 'INVENTORY.DATE' },
    { field: 'warehouse', header: 'INVENTORY.WAREHOUSE' },
    { field: 'reference_type', header: 'INVENTORY.REFERENCE_TYPE' },
    { field: 'total_quantity', header: 'INVENTORY.TOTAL_QUANTITY' },
    { field: 'total_cost', header: 'INVENTORY.TOTAL_COST' },
    { field: 'status', header: 'COMMON.STATUS' },
    { field: 'actions', header: 'COMMON.ACTIONS' },
  ];

  private fb = inject(FormBuilder);
  private store = inject(Store);
  private messageService = inject(MessageService);
  private confirmationService = inject(ConfirmationService);
  private translate = inject(TranslateService);
  private stockReceiptService = inject(StockReceiptService);
  private productService = inject(ProductService);
  private settingService = inject(SettingService);
  private warehouseService = inject(WarehouseService);
  private stockBalanceService = inject(StockBalanceService);

  units$ = this.store.select(selectAllUnits);

  stockQtyMap = signal<Record<string, number>>({});
  searchedProducts = signal<Product[]>([]);
  minSearchChars = signal(3);
  searchLoading = signal(false);
  private searchSubject = new Subject<string>();

  products = signal<Product[]>([]);
  /** Map product_id → variants for products that have variants */
  productVariantsMap = signal<Record<number, ProductVariant[]>>({});
  /** Map product_id → tracking_type for serial/batch tracking */
  productTrackingMap = signal<Record<number, string>>({});
  /** Map row index → serial numbers array entered by user */
  rowSerialsMap = signal<Record<number, string[]>>({});
  /** Map row index → expiry dates per serial */
  rowSerialsExpiryMap = signal<Record<number, string[]>>({});

  items = signal<StockReceipt[]>([]);
  loading = signal(false);
  saving = signal(false);
  quickMode = signal(false);
  savingStep = signal('');
  dialogVisible = signal(false);
  editing = signal<StockReceipt | null>(null);
  detailDialogVisible = signal(false);
  selectedReceipt = signal<StockReceipt | null>(null);
  warehouses = signal<Warehouse[]>([]);
  partners = signal<Partner[]>([]);

  form!: FormGroup;

  referenceTypeOptions: { label: string; value: string }[] = [];

  ngOnInit(): void {
    this.form = this.fb.group({
      warehouse_id: [null, Validators.required],
      date: [new Date(), Validators.required],
      reference_type: ['other', Validators.required],
      partner_id: [null],
      notes: [''],
      items: this.fb.array([]),
    });

    this.referenceTypeOptions = [
      { label: this.translate.instant('INVENTORY.REF_PURCHASE'), value: 'purchase' },
      { label: this.translate.instant('INVENTORY.REF_RETURN'), value: 'return' },
      { label: this.translate.instant('INVENTORY.REF_ADJUSTMENT'), value: 'adjustment' },
      { label: this.translate.instant('INVENTORY.REF_OPENING'), value: 'opening' },
      { label: this.translate.instant('INVENTORY.REF_OTHER'), value: 'other' },
    ];

    this.store.dispatch(UnitsActions.loadUnits({}));
    this.store.dispatch(PartnersActions.loadPartners({}));

    // Server-side product search pipeline
    this.searchSubject.pipe(
      debounceTime(300),
      filter(q => q.length >= this.minSearchChars()),
      switchMap(q => {
        this.searchLoading.set(true);
        return this.productService.searchForDropdown(q);
      }),
    ).subscribe({
      next: (res) => {
        this.searchedProducts.set(res.data);
        this.products.set(res.data);
        this.searchLoading.set(false);
      },
      error: () => this.searchLoading.set(false),
    });

    // Load min search chars from settings
    this.settingService.getByKey('products.min_search_chars').subscribe({
      next: (res: any) => {
        const val = parseInt(res?.data?.value ?? res?.value, 10);
        if (!isNaN(val) && val > 0) this.minSearchChars.set(val);
      },
      error: () => {},
    });

    // Load quick mode setting
    this.settingService.getByKey('inventory.receipt_auto_approve').subscribe({
      next: (res: any) => {
        this.quickMode.set((res?.data?.value ?? res?.value) === 'true');
      },
      error: () => {},
    });

    this.store.select(selectAllPartners).subscribe(partners => {
      this.partners.set(partners.filter((p: any) => p.is_supplier));
    });

    this.warehouseService.listForCurrentBranch().subscribe({
      next: (res) => this.warehouses.set(res.data),
      error: () => {},
    });

    this.loadData();
  }

  get itemsArray(): FormArray {
    return this.form.get('items') as FormArray;
  }

  loadData(): void {
    this.loading.set(true);
    this.stockReceiptService.listAll().subscribe({
      next: (res) => { this.items.set(res.data); this.loading.set(false); },
      error: (err) => { this.loading.set(false); this.showError(err); },
    });
  }

  private resetDialogState(): void {
    this.form.reset({ date: new Date(), reference_type: 'other' });
    this.itemsArray.clear();
    this.searchedProducts.set([]);
    this.products.set([]);
    this.productVariantsMap.set({});
    this.productTrackingMap.set({});
    this.stockQtyMap.set({});
    this.rowSerialsMap.set({});
    this.rowSerialsExpiryMap.set({});
    this.barcodeValue.set('');
    this.savingStep.set('');
    this.saving.set(false);
  }

  openNew(): void {
    this.editing.set(null);
    this.resetDialogState();
    this.addItem();
    this.dialogVisible.set(true);
  }

  edit(item: StockReceipt): void {
    this.editing.set(item);
    this.resetDialogState();
    this.form.patchValue({
      warehouse_id: item.warehouse_id,
      date: new Date(item.date),
      reference_type: item.reference_type,
      partner_id: item.partner_id,
      notes: item.notes,
    });
    // Pre-load existing line products into searchedProducts for dropdown display
    if (item.items && item.items.length > 0) {
      const existingProducts: Product[] = item.items
        .filter(line => line.product_id)
        .map(line => ({
          id: line.product_id,
          name_ar: line.product_name || '',
          name_en: line.product_name || '',
          code: line.product_code || '',
          category: (line as any).category || null,
        } as any as Product));
      this.searchedProducts.set(existingProducts);
      this.products.set(existingProducts);

      item.items.forEach((line, idx) => {
        this.itemsArray.push(this.fb.group({
          product_id: [line.product_id, Validators.required],
          product_variant_id: [line.product_variant_id],
          unit_id: [line.unit_id, Validators.required],
          quantity: [line.quantity, [Validators.required, Validators.min(0.01)]],
          unit_cost: [line.unit_cost, [Validators.required, Validators.min(0)]],
          batch_number: [line.batch_number || ''],
          expiry_date: [line.expiry_date ? new Date(line.expiry_date) : null],
          notes: [line.notes || ''],
          tracking_type: ['none'],
        }));
        // Load serial_numbers from existing receipt item
        if ((line as any).serial_numbers?.length) {
          this.rowSerialsMap.update(m => ({ ...m, [idx]: (line as any).serial_numbers }));
        }
        // Load variants and tracking type
        this.onProductChange(this.itemsArray.length - 1, line.product_id);
      });
    } else {
      this.addItem();
    }
    this.dialogVisible.set(true);
  }

  onSave(): void {
    if (!this.form.valid || this.itemsArray.length === 0) {
      this.form.markAllAsTouched();
      return;
    }
    this.saving.set(true);
    const val = this.form.value;
    const data = {
      warehouse_id: val.warehouse_id,
      date: this.formatDate(val.date),
      reference_type: val.reference_type,
      partner_id: val.partner_id || null,
      notes: val.notes || null,
      items: val.items.map((item: any, idx: number) => {
        const lineItem: any = {
          product_id: item.product_id,
          product_variant_id: item.product_variant_id || null,
          unit_id: item.unit_id,
          quantity: item.quantity,
          unit_cost: item.unit_cost,
          batch_number: item.batch_number || null,
          expiry_date: item.expiry_date ? this.formatDate(item.expiry_date) : null,
          notes: item.notes || null,
        };
        const serials = this.getRowSerials(idx);
        if (serials.length > 0 && this.isSerialTracked(item.product_id)) {
          lineItem.serial_numbers = serials;
        }
        return lineItem;
      }),
    };

    if (this.editing()) {
      // Edit mode — just update, no auto-chain
      this.stockReceiptService.update(this.editing()!.id, data).subscribe({
        next: () => {
          this.saving.set(false);
          this.dialogVisible.set(false);
          this.messageService.add({
            severity: 'success',
            summary: this.translate.instant('COMMON.SUCCESS'),
            detail: this.translate.instant('INVENTORY.RECEIPT_UPDATE_SUCCESS'),
          });
          this.loadData();
        },
        error: (err) => { this.saving.set(false); this.showError(err); },
      });
      return;
    }

    if (this.quickMode()) {
      this.quickSave(data);
    } else {
      this.normalSave(data);
    }
  }

  private normalSave(data: any): void {
    this.savingStep.set(this.translate.instant('INVENTORY.STEP_CREATING_RECEIPT'));
    this.stockReceiptService.create(data).subscribe({
      next: () => {
        this.saving.set(false);
        this.savingStep.set('');
        this.dialogVisible.set(false);
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: this.translate.instant('INVENTORY.RECEIPT_CREATE_SUCCESS'),
        });
        this.loadData();
      },
      error: (err) => { this.saving.set(false); this.savingStep.set(''); this.showError(err); },
    });
  }

  private quickSave(data: any): void {
    // Step 1: Create
    this.savingStep.set(this.translate.instant('INVENTORY.STEP_CREATING_RECEIPT'));
    this.stockReceiptService.create(data).subscribe({
      next: (createRes) => {
        const receiptId = createRes.data.id;
        // Step 2: Approve
        this.savingStep.set(this.translate.instant('INVENTORY.STEP_APPROVING_RECEIPT'));
        this.stockReceiptService.approve(receiptId).subscribe({
          next: () => {
            this.saving.set(false);
            this.savingStep.set('');
            this.dialogVisible.set(false);
            this.messageService.add({
              severity: 'success',
              summary: this.translate.instant('COMMON.SUCCESS'),
              detail: this.translate.instant('INVENTORY.QUICK_RECEIPT_SUCCESS'),
            });
            this.loadData();
          },
          error: (err) => {
            // Approve failed — receipt created as draft
            this.saving.set(false);
            this.savingStep.set('');
            this.dialogVisible.set(false);
            this.showError(err);
            this.messageService.add({
              severity: 'warn',
              summary: this.translate.instant('COMMON.WARNING'),
              detail: this.translate.instant('INVENTORY.QUICK_RECEIPT_PARTIAL'),
            });
            this.loadData();
          },
        });
      },
      error: (err) => { this.saving.set(false); this.savingStep.set(''); this.showError(err); },
    });
  }

  delete(item: StockReceipt): void {
    this.confirmationService.confirm({
      message: this.translate.instant('INVENTORY.CONFIRM_DELETE_RECEIPT'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-exclamation-triangle',
      acceptButtonStyleClass: 'p-button-danger',
      accept: () => {
        this.saving.set(true);
        this.stockReceiptService.delete(item.id).subscribe({
          next: () => {
            this.saving.set(false);
            this.items.update(items => items.filter(i => i.id !== item.id));
            this.messageService.add({
              severity: 'success',
              summary: this.translate.instant('COMMON.SUCCESS'),
              detail: this.translate.instant('INVENTORY.RECEIPT_DELETE_SUCCESS'),
            });
          },
          error: (err) => { this.saving.set(false); this.showError(err); },
        });
      },
    });
  }

  approve(item: StockReceipt): void {
    this.confirmationService.confirm({
      message: this.translate.instant('INVENTORY.CONFIRM_APPROVE_RECEIPT'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-check-circle',
      accept: () => {
        this.saving.set(true);
        this.stockReceiptService.approve(item.id).subscribe({
          next: (res) => {
            this.saving.set(false);
            this.items.update(items => items.map(i => i.id === res.data.id ? res.data : i));
            this.messageService.add({
              severity: 'success',
              summary: this.translate.instant('COMMON.SUCCESS'),
              detail: this.translate.instant('INVENTORY.RECEIPT_APPROVE_SUCCESS'),
            });
          },
          error: (err) => { this.saving.set(false); this.showError(err); },
        });
      },
    });
  }

  cancel(item: StockReceipt): void {
    this.confirmationService.confirm({
      message: this.translate.instant('INVENTORY.CONFIRM_CANCEL_RECEIPT'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-exclamation-triangle',
      acceptButtonStyleClass: 'p-button-danger',
      accept: () => {
        this.saving.set(true);
        this.stockReceiptService.cancel(item.id).subscribe({
          next: (res) => {
            this.saving.set(false);
            this.items.update(items => items.map(i => i.id === res.data.id ? res.data : i));
            this.messageService.add({
              severity: 'success',
              summary: this.translate.instant('COMMON.SUCCESS'),
              detail: this.translate.instant('INVENTORY.RECEIPT_CANCEL_SUCCESS'),
            });
          },
          error: (err) => { this.saving.set(false); this.showError(err); },
        });
      },
    });
  }

  viewDetail(item: StockReceipt): void {
    this.stockReceiptService.getById(item.id).subscribe({
      next: (res) => {
        this.selectedReceipt.set(res.data);
        this.detailDialogVisible.set(true);
      },
      error: (err) => this.showError(err),
    });
  }

  addItem(): void {
    this.itemsArray.push(this.fb.group({
      product_id: [null, Validators.required],
      product_variant_id: [null],
      unit_id: [null, Validators.required],
      quantity: [1, [Validators.required, Validators.min(0.01)]],
      unit_cost: [0, [Validators.required, Validators.min(0)]],
      batch_number: [''],
      expiry_date: [null],
      notes: [''],
      tracking_type: ['none'],
    }));
  }

  ngOnDestroy(): void {
    this.searchSubject.complete();
  }

  onProductSearch(event: { filter: string }): void {
    const q = event.filter?.trim() || '';
    if (q.length < this.minSearchChars()) {
      this.searchedProducts.set([]);
      return;
    }
    this.searchSubject.next(q);
  }

  onProductChange(index: number, productId: number): void {
    const group = this.itemsArray.at(index) as FormGroup;
    group.patchValue({ product_variant_id: null });

    // Clear serials for this row
    this.rowSerialsMap.update(m => { const u = { ...m }; delete u[index]; return u; });
    this.rowSerialsExpiryMap.update(m => { const u = { ...m }; delete u[index]; return u; });

    // Always fetch full product detail — ensures unit, price, tracking_type, variants are all loaded
    this.productService.getById(productId).subscribe({
      next: async (res) => {
        const p = res.data;

        // Update product in local cache with full data
        this.products.update(arr => arr.map(x => x.id === p.id ? p : x));
        if (!this.products().find(x => x.id === p.id)) {
          this.products.update(arr => [...arr, p]);
        }

        // Tracking type
        const tt = p.tracking_type?.value || 'none';
        this.productTrackingMap.update(m => ({ ...m, [productId]: tt }));
        group.patchValue({ tracking_type: tt });

        // Variants
        if (p.has_variants && !this.productVariantsMap()[productId]) {
          if (p.variants?.length) {
            this.productVariantsMap.update(m => ({ ...m, [productId]: p.variants! }));
          } else {
            this.productService.listVariants(productId).subscribe({
              next: (vRes) => this.productVariantsMap.update(m => ({ ...m, [productId]: vRes.data })),
            });
          }
        }

        // Unit — always set when user picks a product (onChange),
        // skip only during edit() pre-load when value is already populated
        const isNewSelection = !this.editing() || !group.get('unit_id')?.value;
        if (isNewSelection) {
          if (p.base_unit_id) {
            group.patchValue({ unit_id: p.base_unit_id });
          } else if (p.product_units?.length) {
            group.patchValue({ unit_id: p.product_units[0].unit_id });
          } else {
            // Last resort: use first unit from store
            const units = await firstValueFrom(this.units$);
            if (units?.length) group.patchValue({ unit_id: units[0].id });
          }
        }

        // Purchase price — always set when user picks a product (onChange),
        // skip only during edit() pre-load when value is already populated
        const isNewCost = !this.editing() || !group.get('unit_cost')?.value || group.get('unit_cost')?.value === 0;
        if (isNewCost) {
          const cost = parseFloat(p.purchase_price) || 0;
          if (cost > 0) group.patchValue({ unit_cost: cost });
        }
      },
      error: () => {
        group.patchValue({ tracking_type: 'none' });
      },
    });

    this.loadStockForProduct(productId);
  }

  private loadStockForProduct(productId: number): void {
    const warehouseId = this.form.get('warehouse_id')?.value;
    const filters: any = { product_id: productId };
    if (warehouseId) filters.warehouse_id = warehouseId;
    this.stockBalanceService.listFiltered(filters).subscribe({
      next: (res) => {
        this.stockQtyMap.update(m => {
          const updated = { ...m };
          let total = 0;
          res.data.forEach(b => {
            const key = b.product_variant_id ? `${productId}-${b.product_variant_id}` : `${productId}`;
            updated[key] = +b.available_quantity || 0;
            total += +b.available_quantity || 0;
          });
          updated[`${productId}`] = total;
          return updated;
        });
      },
    });
  }

  getStockQty(productId: number, variantId: number | null): number | null {
    if (!productId) return null;
    const key = variantId ? `${productId}-${variantId}` : `${productId}`;
    return this.stockQtyMap()[key] ?? null;
  }

  getProductBarcode(productId: number): string | null {
    if (!productId) return null;
    const product = this.products().find(p => p.id === productId);
    return product?.barcode || null;
  }

  getProductVariants(productId: number): ProductVariant[] {
    return this.productVariantsMap()[productId] || [];
  }

  isVariantProduct(productId: number): boolean {
    const product = this.products().find(p => p.id === productId);
    return !!product?.has_variants;
  }

  removeItem(index: number): void {
    this.itemsArray.removeAt(index);
  }

  @ViewChild('barcodeInput') barcodeInput!: ElementRef<HTMLInputElement>;
  barcodeValue = signal('');
  barcodeLoading = signal(false);

  onBarcodeScan(event: Event): void {
    event.preventDefault();
    const barcode = this.barcodeValue().trim();
    if (!barcode) return;

    this.barcodeLoading.set(true);
    this.productService.lookupByBarcode(barcode).subscribe({
      next: (result) => {
        this.barcodeLoading.set(false);
        if (result) {
          if (result.variants) {
            this.productVariantsMap.update(m => ({ ...m, [result.product.id]: result.variants! }));
          }
          this.addProductByBarcode(result.product, result.variantId);
        } else {
          this.messageService.add({
            severity: 'warn',
            summary: this.translate.instant('COMMON.WARNING'),
            detail: this.translate.instant('INVENTORY.BARCODE_NOT_FOUND'),
          });
          this.barcodeValue.set('');
          setTimeout(() => this.barcodeInput?.nativeElement?.focus(), 50);
        }
      },
      error: () => {
        this.barcodeLoading.set(false);
        this.messageService.add({
          severity: 'error',
          summary: this.translate.instant('COMMON.ERROR'),
          detail: this.translate.instant('INVENTORY.BARCODE_SEARCH_FAILED'),
        });
      },
    });
  }

  private addProductByBarcode(product: Product, variantId: number | null): void {
    // Add product to searchedProducts if not already there
    const current = this.searchedProducts();
    if (!current.find(p => p.id === product.id)) {
      this.searchedProducts.set([...current, product]);
      this.products.set([...this.products(), product]);
    }

    // Check if product+variant already exists in a row → increment quantity
    for (let i = 0; i < this.itemsArray.length; i++) {
      const row = this.itemsArray.at(i) as FormGroup;
      if (row.get('product_id')?.value === product.id &&
          (row.get('product_variant_id')?.value || null) === (variantId || null)) {
        const currentQty = row.get('quantity')?.value || 0;
        row.patchValue({ quantity: currentQty + 1 });
        this.barcodeValue.set('');
        setTimeout(() => this.barcodeInput?.nativeElement?.focus(), 50);
        return;
      }
    }

    const lastIndex = this.itemsArray.length - 1;
    const lastRow = lastIndex >= 0 ? this.itemsArray.at(lastIndex) as FormGroup : null;
    const useExistingRow = lastRow && !lastRow.get('product_id')?.value;

    const fillRow = (unitId: number | null, unitCost: number) => {
      let rowIndex: number;
      if (useExistingRow) {
        lastRow!.patchValue({
          product_id: product.id,
          product_variant_id: variantId,
          unit_id: unitId,
          quantity: 1,
          unit_cost: unitCost,
        });
        rowIndex = lastIndex;
      } else {
        this.itemsArray.push(this.fb.group({
          product_id: [product.id, Validators.required],
          product_variant_id: [variantId],
          unit_id: [unitId, Validators.required],
          quantity: [1, [Validators.required, Validators.min(0.01)]],
          unit_cost: [unitCost, [Validators.required, Validators.min(0)]],
          batch_number: [''],
          expiry_date: [null],
          notes: [''],
          tracking_type: ['none'],
        }));
        rowIndex = this.itemsArray.length - 1;
      }
      // Load tracking_type for barcode-added product
      this.onProductChange(rowIndex, product.id);
      this.barcodeValue.set('');
      setTimeout(() => this.barcodeInput?.nativeElement?.focus(), 50);
    };

    // Resolve unit
    const resolveUnit = (callback: (unitId: number | null) => void) => {
      if (product.base_unit_id) {
        callback(product.base_unit_id);
      } else {
        this.productService.listProductUnits(product.id).subscribe({
          next: async (res) => {
            if (res.data?.length > 0) {
              callback(res.data[0].unit_id);
            } else {
              const units = await firstValueFrom(this.units$);
              callback(units?.[0]?.id || null);
            }
          },
          error: async () => {
            const units = await firstValueFrom(this.units$);
            callback(units?.[0]?.id || null);
          },
        });
      }
    };

    // Resolve price: for receipts use purchase price
    // Main product purchase_price first, then check variant purchase prices
    const baseCost = parseFloat(product.purchase_price) || 0;

    if (variantId) {
      // Fetch variant prices to find purchase-type price for this variant
      this.productService.listPrices(product.id).subscribe({
        next: (pricesRes) => {
          let unitCost = baseCost;
          // Look for purchase price for this variant
          const purchasePrice = pricesRes.data.find(
            p => p.product_variant_id === variantId &&
              (typeof p.price_type === 'string' ? p.price_type : p.price_type?.value) === 'purchase'
          );
          if (purchasePrice) {
            unitCost = parseFloat(purchasePrice.price) || 0;
          }
          resolveUnit(unitId => fillRow(unitId, unitCost));
        },
        error: () => resolveUnit(unitId => fillRow(unitId, baseCost)),
      });
    } else {
      resolveUnit(unitId => fillRow(unitId, baseCost));
    }
  }

  // ─── Serial Number Dialog ───

  serialDialogVisible = signal(false);
  serialDialogRowIndex = signal(-1);
  serialDialogProductName = signal('');
  serialDialogQty = signal(0);
  serialDialogRows = signal<{ serial_number: string; expiry_date: string }[]>([]);

  getRowSerials(index: number): string[] {
    return this.rowSerialsMap()[index] || [];
  }

  isSerialTracked(productId: number): boolean {
    return (this.productTrackingMap()[productId] || 'none') === 'serial';
  }

  openSerialDialog(rowIndex: number): void {
    const group = this.itemsArray.at(rowIndex) as FormGroup;
    const productId = group.get('product_id')?.value;
    const qty = Math.floor(+(group.get('quantity')?.value || 0));
    if (qty <= 0) {
      this.messageService.add({ severity: 'warn', summary: this.translate.instant('COMMON.WARNING'), detail: this.translate.instant('INVENTORY.ENTER_QUANTITY_FIRST') });
      return;
    }

    const product = this.products().find(p => p.id === productId);
    const productCode = product?.code || 'SN';
    this.serialDialogRowIndex.set(rowIndex);
    this.serialDialogProductName.set(product?.name_ar || product?.name_en || '');
    this.serialDialogQty.set(qty);

    const existing = this.getRowSerials(rowIndex);
    const existingExpiry = (this.rowSerialsExpiryMap()[rowIndex] || []);

    // If all rows already have serials, just show them
    if (existing.length >= qty) {
      const rows: { serial_number: string; expiry_date: string }[] = [];
      for (let i = 0; i < qty; i++) {
        rows.push({ serial_number: existing[i], expiry_date: existingExpiry[i] || '' });
      }
      this.serialDialogRows.set(rows);
      this.serialDialogVisible.set(true);
      return;
    }

    // Fetch existing serials to find the max number and avoid duplicates
    this.productService.listSerials(productId, {}).subscribe({
      next: (res) => {
        const allSerials = res.data || [];
        // Find the highest serial suffix for this product code + today's date
        const now = new Date();
        const datePart = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;
        const prefix = `${productCode}-${datePart}-`;
        let maxNum = 0;
        allSerials.forEach((s: any) => {
          const sn: string = s.serial_number || '';
          if (sn.startsWith(prefix)) {
            const num = parseInt(sn.substring(prefix.length), 10);
            if (!isNaN(num) && num > maxNum) maxNum = num;
          }
        });

        // Also check serials already assigned in other rows of this form
        const allFormSerials = Object.values(this.rowSerialsMap()).flat();
        allFormSerials.forEach(sn => {
          if (sn.startsWith(prefix)) {
            const num = parseInt(sn.substring(prefix.length), 10);
            if (!isNaN(num) && num > maxNum) maxNum = num;
          }
        });

        const rows: { serial_number: string; expiry_date: string }[] = [];
        for (let i = 0; i < qty; i++) {
          rows.push({
            serial_number: existing[i] || `${prefix}${String(maxNum + (i - existing.length) + 1).padStart(3, '0')}`,
            expiry_date: existingExpiry[i] || '',
          });
        }
        this.serialDialogRows.set(rows);
        this.serialDialogVisible.set(true);
      },
      error: () => {
        // Fallback: just start from 001
        const now = new Date();
        const datePart = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;
        const rows: { serial_number: string; expiry_date: string }[] = [];
        for (let i = 0; i < qty; i++) {
          rows.push({
            serial_number: existing[i] || `${productCode}-${datePart}-${String(i + 1).padStart(3, '0')}`,
            expiry_date: existingExpiry[i] || '',
          });
        }
        this.serialDialogRows.set(rows);
        this.serialDialogVisible.set(true);
      },
    });
  }

  updateSerialRow(index: number, field: 'serial_number' | 'expiry_date', value: string): void {
    this.serialDialogRows.update(rows => {
      const updated = [...rows];
      updated[index] = { ...updated[index], [field]: value };
      return updated;
    });
  }

  confirmSerialDialog(): void {
    const rows = this.serialDialogRows();
    const rowIndex = this.serialDialogRowIndex();
    // Validate: all serial numbers must be filled
    const empty = rows.some(r => !r.serial_number.trim());
    if (empty) {
      this.messageService.add({ severity: 'warn', summary: this.translate.instant('COMMON.WARNING'), detail: this.translate.instant('INVENTORY.FILL_ALL_SERIALS') });
      return;
    }
    // Check for duplicates
    const sns = rows.map(r => r.serial_number.trim());
    const unique = new Set(sns);
    if (unique.size !== sns.length) {
      this.messageService.add({ severity: 'warn', summary: this.translate.instant('COMMON.WARNING'), detail: this.translate.instant('INVENTORY.SERIAL_DUPLICATE') });
      return;
    }
    // Save serials and expiry dates
    this.rowSerialsMap.update(m => ({ ...m, [rowIndex]: sns }));
    this.rowSerialsExpiryMap.update(m => ({ ...m, [rowIndex]: rows.map(r => r.expiry_date) }));
    this.serialDialogVisible.set(false);
    this.messageService.add({ severity: 'success', summary: this.translate.instant('COMMON.SUCCESS'), detail: `${sns.length} ${this.translate.instant('INVENTORY.SERIAL_NUMBERS')}` });
  }

  getStatusSeverity(status: string): 'success' | 'warn' | 'danger' | 'info' {
    switch (status) {
      case 'approved': return 'success';
      case 'cancelled': return 'danger';
      default: return 'warn';
    }
  }

  private showError(err: any): void {
    const detail = err.error?.message || err.error?.errors
      ? (err.error.message || Object.values(err.error.errors || {}).flat().join(', '))
      : 'Failed';
    this.messageService.add({
      severity: 'error',
      summary: this.translate.instant('COMMON.ERROR'),
      detail,
    });
  }

  private formatDate(date: Date): string {
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, '0');
    const d = String(date.getDate()).padStart(2, '0');
    return `${y}-${m}-${d}`;
  }
}
