import { Component, inject, OnInit, OnDestroy, signal, computed, ViewChild, ElementRef } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { CommonModule } from '@angular/common';
import {
  FormBuilder,
  FormGroup,
  FormArray,
  ReactiveFormsModule,
  FormsModule,
  Validators,
} from '@angular/forms';
import { Store } from '@ngrx/store';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http';
import { Subject, debounceTime, filter, switchMap, Subscription, concat, of } from 'rxjs';
import { ProductService } from '../../../core/services/product.service';
import { PartnerService } from '../../../core/services/partner.service';
import { SettingService } from '../../../core/services/setting.service';
import { BankAccountService } from '../../../core/services/bank-account.service';
import { WarehouseService } from '../../../core/services/warehouse.service';
import { StockBalanceService } from '../../../core/services/stock-balance.service';
import { SalesSettingsService } from '../../../core/services/sales-settings.service';
import { ReportService } from '../../../core/services/report.service';
import { Product } from '../../../core/models/product.model';
import { Warehouse } from '../../../core/models/warehouse.model';
import { BankAccount } from '../../../core/models/bank-account.model';
import { Account } from '../../../core/models/account.model';
import { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button';
import { DialogModule } from 'primeng/dialog';
import { InputTextModule } from 'primeng/inputtext';
import { InputNumberModule } from 'primeng/inputnumber';
import { TextareaModule } from 'primeng/textarea';
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 { TooltipModule } from 'primeng/tooltip';
import { TabsModule } from 'primeng/tabs';
import { ProgressBarModule } from 'primeng/progressbar';
import { ToggleSwitchModule } from 'primeng/toggleswitch';
import { PopoverModule } from 'primeng/popover';
import { MessageService, ConfirmationService } from 'primeng/api';
import {
  PageHeaderComponent,
  DataTableComponent,
  FormDialogComponent,
  TableColumn,
  SalesFilterBarComponent,
} from '../../../shared';
import {
  PartnersActions,
  selectAllPartners,
  CurrenciesActions,
  selectAllCurrencies,
  UsersActions,
  selectAllUsers,
  TaxRatesActions,
  selectAllTaxRates,
  UnitsActions,
  selectAllUnits,
  AccountsActions,
  selectAllAccounts,
  DashboardActions,
  selectDashboardCompany,
} from '../../../core/store';
import {
  SalesInvoice,
  SalesInvoiceItemPayload,
} from '../../../core/models/sales-invoice.model';
import { SalesPayment } from '../../../core/models/sales-payment.model';
import { SalesInvoiceService } from '../../../core/services/sales-invoice.service';
import { JournalEntryService } from '../../../core/services/journal-entry.service';
import { JournalEntry } from '../../../core/models/journal-entry.model';
import { PrintService } from '../../../shared/print/print.service';
import { ModuleColorService } from '../../../core/services/module-color.service';
import { INVOICE_PRINT_CONFIG } from './invoice-print.config';

@Component({
  selector: 'app-invoices',
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    FormsModule,
    TranslateModule,
    TableModule,
    ButtonModule,
    DialogModule,
    InputTextModule,
    InputNumberModule,
    TextareaModule,
    SelectModule,
    DatePickerModule,
    TagModule,
    ToastModule,
    ConfirmDialogModule,
    TooltipModule,
    TabsModule,
    ProgressBarModule,
    ToggleSwitchModule,
    PopoverModule,
    PageHeaderComponent,
    DataTableComponent,
    FormDialogComponent,
    SalesFilterBarComponent,
  ],
  providers: [MessageService, ConfirmationService],
  templateUrl: './invoices.component.html',
  styleUrl: './invoices.component.scss',
})
export class InvoicesComponent implements OnInit, OnDestroy {
  private fb = inject(FormBuilder);
  private store = inject(Store);
  private messageService = inject(MessageService);
  private confirmationService = inject(ConfirmationService);
  private translate = inject(TranslateService);
  private service = inject(SalesInvoiceService);
  private productService = inject(ProductService);
  private partnerService = inject(PartnerService);
  private settingService = inject(SettingService);
  private bankAccountService = inject(BankAccountService);
  private warehouseService = inject(WarehouseService);
  private stockBalanceService = inject(StockBalanceService);
  private salesSettingsService = inject(SalesSettingsService);
  private reportService = inject(ReportService);
  private route = inject(ActivatedRoute);
  private http = inject(HttpClient);
  private journalEntryService = inject(JournalEntryService);
  private printService = inject(PrintService);
  private moduleColorService = inject(ModuleColorService);
  private elRef = inject(ElementRef);
  private langSub!: Subscription;

  readonly columns: TableColumn[] = [
    { field: 'invoice_number', header: 'SALES.INVOICE_NUMBER' },
    { field: 'date', header: 'SALES.DATE', sortable: true },
    { field: 'due_date', header: 'SALES.DUE_DATE', sortable: true },
    { field: 'customer_name', header: 'SALES.CUSTOMER' },
    { field: 'total', header: 'SALES.TOTAL', sortable: true },
    { field: 'amount_paid', header: 'SALES.AMOUNT_PAID' },
    { field: 'balance_due', header: 'SALES.BALANCE_DUE' },
    { field: 'status', header: 'SALES.STATUS' },
    { field: 'payment_status', header: 'SALES.PAYMENT_STATUS' },
    { field: 'actions', header: 'COMMON.ACTIONS' },
  ];

  partners$ = this.store.select(selectAllPartners);
  currencies$ = this.store.select(selectAllCurrencies);
  users$ = this.store.select(selectAllUsers);
  taxRates$ = this.store.select(selectAllTaxRates);
  units$ = this.store.select(selectAllUnits);
  accounts$ = this.store.select(selectAllAccounts);

  // Customer balance info
  customerBalance = signal<number | null>(null);
  customerBalanceLoading = signal(false);

  // Product stock per line (index → stock qty)
  lineStockQty = signal<Map<number, number | null>>(new Map());

  // Warehouses (filtered by branch)
  warehouses = signal<Warehouse[]>([]);

  // Price type options
  priceTypeOptions = [
    { label: 'PRODUCTS.PRICE_RETAIL', value: 'retail' },
    { label: 'PRODUCTS.PRICE_WHOLESALE', value: 'wholesale' },
    { label: 'PRODUCTS.PRICE_HALF_WHOLESALE', value: 'half_wholesale' },
  ];

  // Server-side product search
  searchedProducts = signal<Product[]>([]);
  searchLoading = signal(false);
  minSearchChars = signal(3);
  private searchSubject = new Subject<string>();
  private searchSub!: Subscription;

  // Server-side pagination
  tableData = signal<SalesInvoice[]>([]);
  totalRecords = signal(0);
  tableLoading = signal(false);
  first = 0;
  rows = 25;

  // Filter bar
  activeFilters: Record<string, any> = {};
  filterCustomers = signal<any[]>([]);
  readonly statusOptions = [
    { label: 'مسودة', value: 'draft' },
    { label: 'قيد الموافقة', value: 'pending_approval' },
    { label: 'موافق عليه', value: 'approved' },
    { label: 'مرحّل', value: 'posted' },
    { label: 'ملغي', value: 'cancelled' },
  ];

  // Create/Edit dialog
  dialogVisible = signal(false);
  editingInvoice = signal<SalesInvoice | null>(null);
  saving = signal(false);
  invoiceForm!: FormGroup;

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

  // Detail view dialog
  viewDialogVisible = signal(false);
  viewingInvoice = signal<SalesInvoice | null>(null);
  invoicePayments = signal<SalesPayment[]>([]);
  paymentSchedule = signal<any[]>([]);
  loadingPayments = signal(false);
  revenueJe = signal<JournalEntry | null>(null);
  cogsJe = signal<JournalEntry | null>(null);
  jeLoading = signal(false);

  // Record Payment dialog
  paymentDialogVisible = signal(false);
  paymentInvoice = signal<SalesInvoice | null>(null);
  paymentForm!: FormGroup;
  savingPayment = signal(false);
  defaultBankAccountId = signal<number | null>(null);
  defaultCheckAccountId = signal<number | null>(null);

  // Server-side customer search
  searchedCustomers = signal<any[]>([]);
  customerSearchLoading = signal(false);
  private customerSearchSubject = new Subject<string>();
  private customerSearchSub!: Subscription;

  // Quick add customer dialog
  quickAddVisible = signal(false);
  quickAddSaving = signal(false);
  quickAddForm!: FormGroup;

  // Bank accounts & payment destinations
  bankAccounts = signal<BankAccount[]>([]);
  loadedAccounts = signal<Account[]>([]);
  paymentDestinations = signal<{ id: string; label: string; sublabel: string; icon: string; payment_method: string; receiving_account_id: number; bank_account_id: number | null }[]>([]);

  // Approval workflow setting
  approvalWorkflow = signal<string>('none');

  // Sales defaults from settings
  salesDefaults = signal<Record<string, any>>({});
  baseCurrency = signal<string>('SAR');

  // Invoice field visibility settings
  fieldVisibility = signal<Record<string, boolean>>({
    warehouse: true,
    currency: true,
    price_type: true,
    salesperson: true,
    payment_terms: true,
    tax: true,
    discount: true,
  });
  fieldSettingsVisible = signal(false);

  // Overall invoice-level discount
  overallDiscountType = signal<'percent' | 'amount'>('amount');
  overallDiscountValue = signal<number>(0);

  // Inline payment section in invoice form
  inlinePayEnabled = signal(false);
  inlinePayAmount = signal<number>(0);
  inlinePayDestId = signal<string | null>(null);
  inlinePayCheckNumber = signal('');
  inlinePayCheckDate = signal<Date | null>(null);
  inlinePayCheckBank = signal('');
  inlinePayReference = signal('');

  // Print
  pendingPrintAfterSave = signal(false);
  companyInfo = signal<any>(null);

  // Quick stats
  totalOutstanding = signal(0);
  overdueAmount = signal(0);

  ngOnInit(): void {
    this.elRef.nativeElement.style.setProperty('--module-color', this.moduleColorService.salesColor());
    this.initForm();
    this.initPaymentForm();
    this.initQuickAddForm();
    this.langSub = this.translate.onLangChange.subscribe(() => {});
    this.loadPage();
    this.store.dispatch(PartnersActions.loadPartners({}));
    this.partners$.subscribe(p => this.filterCustomers.set(p));
    this.store.dispatch(DashboardActions.loadCompany());
    this.store.select(selectDashboardCompany).subscribe(c => {
      if (c) this.companyInfo.set(c);
    });
    this.printService.getCompanyWithLogo().subscribe(c => {
      if (c) this.companyInfo.set(c);
    });
    this.store.dispatch(CurrenciesActions.loadCurrencies({}));
    this.store.dispatch(UsersActions.loadUsers({}));
    this.store.dispatch(TaxRatesActions.loadTaxRates({}));
    this.store.dispatch(UnitsActions.loadUnits({}));
    this.store.dispatch(AccountsActions.loadAccounts({}));

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

    // Load initial customers (filtered)
    this.partnerService.searchForDropdown('', { is_customer: '1' }).subscribe({
      next: (res) => {
        if (res.data.length > 0) this.searchedCustomers.set(res.data);
      },
      error: () => {},
    });

    // Load base currency from currencies list
    this.currencies$.subscribe((currencies) => {
      const base = currencies.find((c: any) => c.is_base);
      if (base) this.baseCurrency.set(base.code);
    });

    // Store accounts in signal for sync access & rebuild destinations
    this.accounts$.subscribe((accs) => {
      this.loadedAccounts.set(accs);
      if (accs.length > 0) this.buildPaymentDestinations();
    });

    // Load bank accounts
    this.bankAccountService.listAll().subscribe({
      next: (res) => {
        this.bankAccounts.set(res.data.filter((ba) => ba.is_active));
        this.buildPaymentDestinations();
      },
      error: () => {},
    });

    // Server-side customer search (customers only)
    this.customerSearchSub = this.customerSearchSubject.pipe(
      debounceTime(300),
      filter(q => q.length >= 2),
      switchMap(q => {
        this.customerSearchLoading.set(true);
        return this.partnerService.searchForDropdown(q, { is_customer: '1' });
      }),
    ).subscribe({
      next: (res) => {
        this.searchedCustomers.set(res.data);
        this.customerSearchLoading.set(false);
      },
      error: () => this.customerSearchLoading.set(false),
    });

    // Server-side product search
    this.searchSub = 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.searchLoading.set(false);
      },
      error: () => this.searchLoading.set(false),
    });

    // Load default accounts for payment dialog
    this.settingService.getByKey('accounting.default_cash_account').subscribe({
      next: (res: any) => {
        const val = parseInt(res?.data?.value ?? res?.value, 10);
        if (!isNaN(val) && val > 0) {
          this.defaultCashAccountId.set(val);
          this.buildPaymentDestinations();
        }
      },
      error: () => {},
    });
    this.settingService.getByKey('accounting.default_bank_account').subscribe({
      next: (res: any) => {
        const val = parseInt(res?.data?.value ?? res?.value, 10);
        if (!isNaN(val) && val > 0) {
          this.defaultBankAccountId.set(val);
          this.buildPaymentDestinations();
        }
      },
      error: () => {},
    });
    this.settingService.getByKey('accounting.default_check_account').subscribe({
      next: (res: any) => {
        const val = parseInt(res?.data?.value ?? res?.value, 10);
        if (!isNaN(val) && val > 0) {
          this.defaultCheckAccountId.set(val);
          this.buildPaymentDestinations();
        }
      },
      error: () => {},
    });

    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 all sales settings (defaults + workflow)
    this.salesSettingsService.getSettings().subscribe({
      next: (res) => {
        const defaults: Record<string, any> = {};
        for (const item of res.data || []) {
          const key = item.definition?.setting_key?.replace('sales.', '') || '';
          defaults[key] = item.current_value;
        }
        this.salesDefaults.set(defaults);
        this.approvalWorkflow.set(defaults['approval_workflow'] || 'none');
      },
      error: () => {},
    });

    // Load invoice field visibility settings
    this.settingService.getByKey('sales.invoice_visible_fields').subscribe({
      next: (res: any) => {
        try {
          const raw = res?.data?.value ?? res?.value;
          const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
          if (parsed && typeof parsed === 'object') {
            this.fieldVisibility.set({ ...this.fieldVisibility(), ...parsed });
          }
        } catch {}
      },
      error: () => {},
    });

    // Handle deep-link from journal entries (viewId query param)
    this.route.queryParams.subscribe(params => {
      const viewId = params['viewId'];
      if (viewId) {
        const id = parseInt(viewId, 10);
        if (!isNaN(id)) {
          this.service.getById(id).subscribe({
            next: (res) => {
              if (res.data) this.viewInvoice(res.data);
            },
          });
        }
      }
    });
  }

  ngOnDestroy(): void {
    this.langSub?.unsubscribe();
    this.searchSub?.unsubscribe();
    this.customerSearchSub?.unsubscribe();
  }

  onCustomerSearch(event: { filter: string }): void {
    const q = event.filter?.trim() || '';
    if (q.length < 2) {
      this.searchedCustomers.set([]);
      return;
    }
    this.customerSearchSubject.next(q);
  }

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

  private initForm(): void {
    const today = new Date();
    const dueDate = new Date();
    dueDate.setDate(dueDate.getDate() + 30);

    this.invoiceForm = this.fb.group({
      customer_id: [null, Validators.required],
      date: [today, Validators.required],
      due_date: [dueDate, Validators.required],
      currency_code: [null],
      exchange_rate: [1],
      salesperson_id: [null],
      warehouse_id: [null],
      price_type: [null],
      payment_terms_days: [30],
      reference: [''],
      subject: [''],
      notes: [''],
      notes_ar: [''],
      terms: [''],
      terms_ar: [''],
      items: this.fb.array([]),
    });
  }

  private initQuickAddForm(): void {
    this.quickAddForm = this.fb.group({
      name_ar: ['', Validators.required],
      name_en: [''],
      phone: [''],
      email: [''],
    });
  }

  private initPaymentForm(): void {
    this.paymentForm = this.fb.group({
      date: [new Date(), Validators.required],
      amount: [0, [Validators.required, Validators.min(0.01)]],
      payment_method: ['cash', Validators.required],
      receiving_account_id: [null, Validators.required],
      bank_account_id: [null],
      check_number: [''],
      check_date: [null],
      check_bank: [''],
      reference: [''],
      notes: [''],
    });
  }

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

  private createItem(item?: any): FormGroup {
    return this.fb.group({
      product_id: [item?.product_id || null, Validators.required],
      unit_id: [item?.unit_id || null, Validators.required],
      warehouse_id: [item?.warehouse_id || null],
      quantity: [item?.quantity || 1, [Validators.required, Validators.min(0.01)]],
      unit_price: [item?.unit_price || 0, [Validators.required, Validators.min(0)]],
      discount_percent: [item?.discount_percent || 0],
      tax_rate_id: [item?.tax_rate_id || null],
      description: [item?.description || ''],
      notes: [item?.notes || ''],
    });
  }

  addItem(): void {
    const defaultWarehouse = this.invoiceForm.get('warehouse_id')?.value;
    const d = this.salesDefaults();
    const defaultTax = d['default_tax_id'] || null;
    this.items.push(this.createItem({
      warehouse_id: defaultWarehouse || null,
      tax_rate_id: defaultTax > 0 ? defaultTax : null,
    }));
  }

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

  getLineTotal(index: number): number {
    const line = this.items.at(index);
    const qty = parseFloat(line.get('quantity')?.value) || 0;
    const price = parseFloat(line.get('unit_price')?.value) || 0;
    const discountPct = parseFloat(line.get('discount_percent')?.value) || 0;
    const subtotal = qty * price;
    const discount = subtotal * (discountPct / 100);
    return subtotal - discount;
  }

  /** Sum of qty × price for all lines (before any discounts) */
  get rawSubtotal(): number {
    let total = 0;
    for (let i = 0; i < this.items.length; i++) {
      const line = this.items.at(i);
      const qty = parseFloat(line.get('quantity')?.value) || 0;
      const price = parseFloat(line.get('unit_price')?.value) || 0;
      total += qty * price;
    }
    return total;
  }

  /** Sum of line totals (after line-level discounts, before overall discount) */
  get subtotal(): number {
    let total = 0;
    for (let i = 0; i < this.items.length; i++) {
      total += this.getLineTotal(i);
    }
    return total;
  }

  /** Total of line-level discounts only */
  get lineDiscountTotal(): number {
    return this.rawSubtotal - this.subtotal;
  }

  /** Combined line + overall discount amount */
  get discountAmount(): number {
    return this.lineDiscountTotal + this.overallDiscountComputed;
  }

  /** Calculated overall discount amount based on type and value */
  get overallDiscountComputed(): number {
    const val = this.overallDiscountValue();
    if (!val || val <= 0) return 0;
    if (this.overallDiscountType() === 'percent') {
      return this.subtotal * (val / 100);
    }
    return Math.min(val, this.subtotal);
  }

  /** Subtotal after all discounts (line + overall) */
  get subtotalAfterDiscount(): number {
    return this.subtotal - this.overallDiscountComputed;
  }

  get taxAmount(): number {
    return 0;
  }

  get total(): number {
    return this.subtotalAfterDiscount + this.taxAmount;
  }

  openNew(): void {
    this.editingInvoice.set(null);
    const d = this.salesDefaults();
    const payDays = d['default_payment_terms_days'] || 30;
    const today = new Date();
    const dueDate = new Date();
    dueDate.setDate(dueDate.getDate() + payDays);
    const warehouseId = d['default_warehouse_id'] || null;

    this.invoiceForm.reset({
      customer_id: null,
      date: today,
      due_date: dueDate,
      currency_code: this.baseCurrency(),
      exchange_rate: 1,
      salesperson_id: null,
      warehouse_id: warehouseId > 0 ? warehouseId : null,
      price_type: d['default_price_type'] || 'retail',
      payment_terms_days: payDays,
      reference: '',
      subject: '',
      notes: '',
      notes_ar: '',
      terms: '',
      terms_ar: '',
    });
    this.items.clear();
    this.addItem();
    this.overallDiscountType.set('amount');
    this.overallDiscountValue.set(0);
    this.dialogVisible.set(true);
  }

  editInvoice(invoice: SalesInvoice): void {
    if (invoice.status !== 'draft') return;
    this.editingInvoice.set(invoice);
    this.overallDiscountType.set('amount');
    this.overallDiscountValue.set(0);
    this.dialogVisible.set(true);
    // Fetch full detail to get items
    this.service.getById(invoice.id).subscribe({
      next: (res) => {
        const inv = res.data;
        this.invoiceForm.patchValue({
          customer_id: inv.customer_id,
          date: new Date(inv.date),
          due_date: new Date(inv.due_date),
          currency_code: inv.currency_code || 'SAR',
          exchange_rate: parseFloat(inv.exchange_rate) || 1,
          salesperson_id: inv.salesperson_id,
          warehouse_id: inv.warehouse_id || null,
          price_type: 'retail',
          payment_terms_days: inv.payment_terms_days || 30,
          reference: inv.reference || '',
          subject: inv.subject || '',
          notes: inv.notes || '',
          notes_ar: inv.notes_ar || '',
          terms: inv.terms || '',
          terms_ar: inv.terms_ar || '',
        });
        this.items.clear();
        (inv.items || []).forEach((item: any) => {
          this.items.push(
            this.createItem({
              product_id: item.product_id,
              unit_id: item.unit_id,
              warehouse_id: item.warehouse_id,
              quantity: parseFloat(item.quantity),
              unit_price: parseFloat(item.unit_price),
              discount_percent: parseFloat(item.discount_percent),
              tax_rate_id: item.tax_rate_id,
              description: item.description,
              notes: item.notes,
            })
          );
        });
        if (this.items.length === 0) {
          this.addItem();
        }
        // Ensure selected customer is in dropdown
        if (inv.customer_id) {
          const existing = this.searchedCustomers();
          if (!existing.find(c => c.id === inv.customer_id)) {
            this.partnerService.getById(inv.customer_id).subscribe({
              next: (r) => this.searchedCustomers.set([...this.searchedCustomers(), r.data]),
            });
          }
        }
        // Pre-load products for dropdown display
        const productIds = (inv.items || []).map((i: any) => i.product_id).filter(Boolean);
        const uniqueIds = [...new Set(productIds)];
        if (uniqueIds.length > 0) {
          import('rxjs').then(({ forkJoin }) => {
            forkJoin(uniqueIds.map(id => this.productService.getById(id))).subscribe({
              next: (results) => {
                this.searchedProducts.set(results.map((r: any) => r.data));
              },
            });
          });
        }
      },
    });
  }

  viewInvoice(invoice: SalesInvoice): void {
    // Fetch full invoice detail
    this.loadingPayments.set(true);
    this.revenueJe.set(null);
    this.cogsJe.set(null);
    this.service.getById(invoice.id).subscribe({
      next: (res) => {
        const inv = res.data;
        this.viewingInvoice.set(inv);
        this.viewDialogVisible.set(true);
        // Load payments
        this.service.getPayments(invoice.id).subscribe({
          next: (payRes) => {
            this.invoicePayments.set(payRes.data || []);
            this.loadingPayments.set(false);
          },
          error: () => this.loadingPayments.set(false),
        });
        // Load payment schedule
        this.service.getPaymentSchedule(invoice.id).subscribe({
          next: (schedRes) => this.paymentSchedule.set(schedRes.data || []),
          error: () => {},
        });
        // Load journal entries
        if (inv.journal_entry_id || inv.cogs_journal_entry_id) {
          this.jeLoading.set(true);
          let pending = 0;
          const done = () => { pending--; if (pending <= 0) this.jeLoading.set(false); };
          if (inv.journal_entry_id) {
            pending++;
            this.journalEntryService.getById(inv.journal_entry_id).subscribe({
              next: (res) => { this.revenueJe.set(res.data); done(); },
              error: () => done(),
            });
          }
          if (inv.cogs_journal_entry_id) {
            pending++;
            this.journalEntryService.getById(inv.cogs_journal_entry_id).subscribe({
              next: (res) => { this.cogsJe.set(res.data); done(); },
              error: () => done(),
            });
          }
          if (pending === 0) this.jeLoading.set(false);
        }
      },
      error: () => this.loadingPayments.set(false),
    });
  }

  loadPage(page = 1, perPage = this.rows): void {
    this.tableLoading.set(true);
    this.service.list(page, perPage, this.activeFilters).subscribe({
      next: (res) => {
        this.tableData.set(res.data);
        this.totalRecords.set(res.meta.total);
        this.tableLoading.set(false);
        // Calculate quick stats
        let outstanding = 0;
        let overdue = 0;
        const today = new Date().toISOString().split('T')[0];
        res.data.forEach((inv) => {
          const balance = parseFloat(inv.balance_due) || 0;
          if (balance > 0) {
            outstanding += balance;
            if (inv.due_date < today && inv.status !== 'cancelled' && inv.status !== 'draft') {
              overdue += balance;
            }
          }
        });
        this.totalOutstanding.set(outstanding);
        this.overdueAmount.set(overdue);
      },
      error: () => this.tableLoading.set(false),
    });
  }

  private reloadCurrentPage(): void {
    const currentPage = Math.floor(this.first / this.rows) + 1;
    this.loadPage(currentPage);
  }

  deleteInvoice(invoice: SalesInvoice): void {
    if (invoice.status !== 'draft') return;
    this.confirmationService.confirm({
      message: this.translate.instant('COMMON.CONFIRM_DELETE_MSG'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-exclamation-triangle',
      acceptButtonStyleClass: 'p-button-danger',
      accept: () => {
        this.service.delete(invoice.id).subscribe({
          next: () => {
            this.messageService.add({
              severity: 'success',
              summary: this.translate.instant('COMMON.SUCCESS'),
              detail: this.translate.instant('SALES.INVOICE_DELETED'),
            });
            this.reloadCurrentPage();
          },
          error: (err) => this.showError(err),
        });
      },
    });
  }

  submitApproval(invoice: SalesInvoice): void {
    this.service.submitApproval(invoice.id).subscribe({
      next: () => {
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: this.translate.instant('SALES.SUBMIT_APPROVAL') + ' - OK',
        });
        this.reloadCurrentPage();
      },
      error: (err) => this.showError(err),
    });
  }

  approveInvoice(invoice: SalesInvoice): void {
    this.service.approve(invoice.id).subscribe({
      next: () => {
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: this.translate.instant('SALES.APPROVE') + ' - OK',
        });
        this.reloadCurrentPage();
      },
      error: (err) => this.showError(err),
    });
  }

  rejectInvoice(invoice: SalesInvoice): void {
    this.service.reject(invoice.id).subscribe({
      next: () => {
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: this.translate.instant('SALES.REJECT') + ' - OK',
        });
        this.reloadCurrentPage();
      },
      error: (err) => this.showError(err),
    });
  }

  postInvoice(invoice: SalesInvoice): void {
    this.confirmationService.confirm({
      message: this.translate.instant('SALES.CONFIRM_POST_INVOICE'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-exclamation-triangle',
      accept: () => {
        const onSuccess = () => {
          this.messageService.add({
            severity: 'success',
            summary: this.translate.instant('COMMON.SUCCESS'),
            detail: this.translate.instant('SALES.INVOICE_POSTED'),
          });
          this.reloadCurrentPage();
          if (this.viewDialogVisible() && this.viewingInvoice()?.id === invoice.id) {
            this.viewInvoice(invoice);
          }
        };

        if (invoice.status === 'draft') {
          // When workflow is none: auto submit → approve → post in sequence
          this.service.submitApproval(invoice.id).pipe(
            switchMap(() => this.service.approve(invoice.id)),
            switchMap(() => this.service.post(invoice.id)),
          ).subscribe({
            next: () => onSuccess(),
            error: (err) => this.showError(err),
          });
        } else if (invoice.status === 'approved') {
          this.service.post(invoice.id).subscribe({
            next: () => onSuccess(),
            error: (err) => this.showError(err),
          });
        }
      },
    });
  }

  cancelInvoice(invoice: SalesInvoice): void {
    this.confirmationService.confirm({
      message: this.translate.instant('COMMON.CONFIRM_ACTION'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-exclamation-triangle',
      acceptButtonStyleClass: 'p-button-danger',
      accept: () => {
        this.service.cancel(invoice.id).subscribe({
          next: () => {
            this.messageService.add({
              severity: 'success',
              summary: this.translate.instant('COMMON.SUCCESS'),
              detail: this.translate.instant('SALES.CANCEL') + ' - OK',
            });
            this.reloadCurrentPage();
          },
          error: (err) => this.showError(err),
        });
      },
    });
  }

  duplicateInvoice(invoice: SalesInvoice): void {
    this.service.duplicate(invoice.id).subscribe({
      next: () => {
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: this.translate.instant('SALES.DUPLICATE') + ' - OK',
        });
        this.reloadCurrentPage();
      },
      error: (err) => this.showError(err),
    });
  }

  // Record Payment
  defaultCashAccountId = signal<number | null>(null);

  private buildPaymentDestinations(): void {
    const accs = this.loadedAccounts();
    if (accs.length === 0) return;
    if (!this.defaultCashAccountId() && !this.defaultBankAccountId() && !this.defaultCheckAccountId()) return;
    const bankAccsApi = this.bankAccounts();

    const getAccountsFor = (parentId: number | null): Account[] => {
      if (!parentId) return [];
      const parent = accs.find((a) => a.id === parentId);
      if (!parent) return [];
      if (parent.account_type === 'header' && parent.has_children) {
        return accs.filter((a) => a.parent_id === parentId);
      }
      return [parent];
    };

    const destinations: typeof this.paymentDestinations extends () => infer T ? T : never = [];

    for (const acc of getAccountsFor(this.defaultCashAccountId())) {
      destinations.push({
        id: `cash_${acc.id}`,
        label: acc.name_ar || acc.name,
        sublabel: acc.code,
        icon: 'pi pi-wallet',
        payment_method: 'cash',
        receiving_account_id: acc.id,
        bank_account_id: null,
      });
    }
    for (const acc of getAccountsFor(this.defaultBankAccountId())) {
      const matchedBank = bankAccsApi.find((ba) => ba.account_id === acc.id);
      destinations.push({
        id: `bank_${acc.id}`,
        label: acc.name_ar || acc.name,
        sublabel: acc.code,
        icon: 'pi pi-building-columns',
        payment_method: 'bank_transfer',
        receiving_account_id: acc.id,
        bank_account_id: matchedBank?.id || null,
      });
    }
    for (const acc of getAccountsFor(this.defaultCheckAccountId())) {
      destinations.push({
        id: `check_${acc.id}`,
        label: acc.name_ar || acc.name,
        sublabel: acc.code,
        icon: 'pi pi-file',
        payment_method: 'check',
        receiving_account_id: acc.id,
        bank_account_id: null,
      });
    }
    this.paymentDestinations.set(destinations);
    // Set default inline pay destination
    if (!this.inlinePayDestId() && destinations.length > 0) {
      this.inlinePayDestId.set(destinations[0].id);
    }
  }

  getInlinePayDest(): { payment_method: string; receiving_account_id: number; bank_account_id: number | null } | null {
    const dest = this.paymentDestinations().find(d => d.id === this.inlinePayDestId());
    return dest ? { payment_method: dest.payment_method, receiving_account_id: dest.receiving_account_id, bank_account_id: dest.bank_account_id } : null;
  }

  onInlinePayToggle(enabled: boolean): void {
    this.inlinePayEnabled.set(enabled);
    if (enabled) {
      this.inlinePayAmount.set(this.total);
      this.buildPaymentDestinations();
    }
  }

  setInlinePayFull(): void {
    this.inlinePayAmount.set(this.total);
  }

  openPaymentDialog(invoice: SalesInvoice): void {
    this.paymentInvoice.set(invoice);
    const balance = parseFloat(invoice.balance_due) || 0;
    this.buildPaymentDestinations();
    const defaultDest = this.paymentDestinations()[0];

    this.paymentForm.reset({
      date: new Date(),
      amount: balance,
      payment_method: defaultDest?.payment_method || 'cash',
      receiving_account_id: defaultDest?.receiving_account_id || null,
      bank_account_id: defaultDest?.bank_account_id || null,
      check_number: '',
      check_date: null,
      check_bank: '',
      reference: '',
      notes: '',
    });
    this.paymentDialogVisible.set(true);
  }

  onDestinationChange(destId: string): void {
    const dest = this.paymentDestinations().find((d) => d.id === destId);
    if (dest) {
      this.paymentForm.patchValue({
        payment_method: dest.payment_method,
        receiving_account_id: dest.receiving_account_id,
        bank_account_id: dest.bank_account_id,
      });
    }
  }

  getSelectedDestinationId(): string | null {
    const recvId = this.paymentForm.get('receiving_account_id')?.value;
    if (!recvId) return this.paymentDestinations()[0]?.id || null;
    const dest = this.paymentDestinations().find((d) => d.receiving_account_id === recvId);
    return dest?.id || this.paymentDestinations()[0]?.id || null;
  }

  onSavePayment(): void {
    if (!this.paymentForm.valid || !this.paymentInvoice()) return;
    this.savingPayment.set(true);
    const inv = this.paymentInvoice()!;
    const formValue = this.paymentForm.value;
    const data: any = {
      date: this.formatDate(formValue.date),
      amount: formValue.amount,
      payment_method: formValue.payment_method,
      invoice_id: inv.id,
      partner_id: inv.customer_id,
      receiving_account_id: formValue.receiving_account_id,
    };
    if (formValue.bank_account_id) data.bank_account_id = formValue.bank_account_id;
    if (formValue.check_number) data.check_number = formValue.check_number;
    if (formValue.check_date) data.check_date = this.formatDate(formValue.check_date);
    if (formValue.check_bank) data.check_bank = formValue.check_bank;
    if (formValue.reference) data.reference = formValue.reference;
    if (formValue.notes) data.notes = formValue.notes;

    this.service.recordPayment(inv.id, data).subscribe({
      next: (res) => {
        // Auto-post the payment so it creates journal entry and updates invoice status
        const paymentId = res.data?.id;
        if (paymentId) {
          this.service.postPayment(paymentId).subscribe({
            next: () => {
              this.savingPayment.set(false);
              this.paymentDialogVisible.set(false);
              this.messageService.add({
                severity: 'success',
                summary: this.translate.instant('COMMON.SUCCESS'),
                detail: this.translate.instant('SALES.PAYMENT_RECORDED'),
              });
              this.reloadCurrentPage();
              if (this.viewDialogVisible() && this.viewingInvoice()?.id === inv.id) {
                this.viewInvoice(inv);
              }
            },
            error: (err) => {
              this.savingPayment.set(false);
              this.showError(err);
            },
          });
        } else {
          this.savingPayment.set(false);
          this.paymentDialogVisible.set(false);
          this.reloadCurrentPage();
        }
      },
      error: (err) => {
        this.savingPayment.set(false);
        this.showError(err);
      },
    });
  }

  private buildInvoicePayload(): any | null {
    if (!this.invoiceForm.valid || this.items.length < 1) {
      this.invoiceForm.markAllAsTouched();
      if (this.items.length === 0) {
        this.messageService.add({
          severity: 'warn',
          summary: this.translate.instant('COMMON.ERROR'),
          detail: this.translate.instant('SALES.NO_ITEMS'),
        });
      }
      return null;
    }
    const formValue = this.invoiceForm.value;

    // Validate: warehouse required for inventory-tracked products
    const products = this.searchedProducts();
    const missingWarehouse = (formValue.items || []).some((item: any) => {
      if (!item.product_id || item.warehouse_id) return false;
      const product = products.find((p) => p.id === item.product_id);
      return product?.track_inventory;
    });
    if (missingWarehouse) {
      this.messageService.add({
        severity: 'warn',
        summary: this.translate.instant('COMMON.ERROR'),
        detail: this.translate.instant('SALES.WAREHOUSE_REQUIRED_FOR_TRACKED'),
      });
      return null;
    }

    const overallDisc = this.overallDiscountComputed;
    const itemsPayload: SalesInvoiceItemPayload[] = formValue.items.map(
      (item: any, idx: number) => {
        let discountPct = item.discount_percent || 0;
        // Distribute overall discount proportionally across line items
        if (overallDisc > 0) {
          const lineGross = (item.quantity || 0) * (item.unit_price || 0);
          const lineAfterDisc = lineGross * (1 - discountPct / 100);
          if (this.subtotal > 0 && lineGross > 0) {
            const lineShare = (lineAfterDisc / this.subtotal) * overallDisc;
            const combinedDisc = (lineGross * discountPct / 100) + lineShare;
            discountPct = Math.round((combinedDisc / lineGross) * 10000) / 100;
          }
        }
        return {
          product_id: item.product_id,
          unit_id: item.unit_id,
          warehouse_id: item.warehouse_id || undefined,
          quantity: item.quantity,
          unit_price: item.unit_price,
          discount_percent: discountPct,
          tax_rate_id: item.tax_rate_id || undefined,
          description: item.description || undefined,
          notes: item.notes || undefined,
          sort_order: idx + 1,
        };
      }
    );
    return {
      date: this.formatDate(formValue.date),
      due_date: this.formatDate(formValue.due_date),
      customer_id: formValue.customer_id,
      salesperson_id: formValue.salesperson_id || undefined,
      warehouse_id: formValue.warehouse_id || undefined,
      currency_code: formValue.currency_code || undefined,
      exchange_rate: formValue.exchange_rate || undefined,
      payment_terms_days: formValue.payment_terms_days || undefined,
      reference: formValue.reference || undefined,
      subject: formValue.subject || undefined,
      notes: formValue.notes || undefined,
      notes_ar: formValue.notes_ar || undefined,
      terms: formValue.terms || undefined,
      terms_ar: formValue.terms_ar || undefined,
      items: itemsPayload,
    };
  }

  onSave(): void {
    const data = this.buildInvoicePayload();
    if (!data) return;

    this.saving.set(true);
    const isNew = !this.editingInvoice();
    const withPay = this.inlinePayEnabled() && this.inlinePayAmount() > 0;
    const obs$ = isNew
      ? this.service.create(data)
      : this.service.update(this.editingInvoice()!.id, data);

    obs$.pipe(
      // Auto-post when workflow=none or when paying inline
      switchMap((res: any) => {
        const invoice: SalesInvoice = res?.data;
        const shouldPost = (isNew && this.approvalWorkflow() === 'none') || withPay;
        if (shouldPost && invoice?.id && (invoice.status === 'draft' || isNew)) {
          return this.service.submitApproval(invoice.id).pipe(
            switchMap(() => this.service.approve(invoice.id)),
            switchMap(() => this.service.post(invoice.id)),
            switchMap(() => this.service.getById(invoice.id)),
          );
        }
        return of(res);
      }),
      // Record inline payment if enabled
      switchMap((res: any) => {
        const invoice: SalesInvoice = res?.data;
        if (!withPay || !invoice?.id) return of(res);
        const dest = this.getInlinePayDest();
        if (!dest) return of(res);
        const payData: any = {
          date: data.date,
          amount: this.inlinePayAmount(),
          payment_method: dest.payment_method,
          invoice_id: invoice.id,
          partner_id: invoice.customer_id,
          receiving_account_id: dest.receiving_account_id,
        };
        if (dest.bank_account_id) payData.bank_account_id = dest.bank_account_id;
        if (dest.payment_method === 'check') {
          if (this.inlinePayCheckNumber()) payData.check_number = this.inlinePayCheckNumber();
          if (this.inlinePayCheckDate()) payData.check_date = this.formatDate(this.inlinePayCheckDate()!);
          if (this.inlinePayCheckBank()) payData.check_bank = this.inlinePayCheckBank();
        }
        if (this.inlinePayReference()) payData.reference = this.inlinePayReference();
        return this.service.recordPayment(invoice.id, payData).pipe(
          switchMap((payRes: any) => {
            const paymentId = payRes.data?.id;
            if (paymentId) {
              return this.service.postPayment(paymentId);
            }
            return of(payRes);
          }),
        );
      }),
    ).subscribe({
      next: (res: any) => {
        this.saving.set(false);
        this.dialogVisible.set(false);
        const msg = withPay
          ? this.translate.instant('SALES.PAYMENT_RECORDED')
          : (isNew && this.approvalWorkflow() === 'none')
            ? this.translate.instant('SALES.INVOICE_POSTED')
            : this.translate.instant(isNew ? 'SALES.INVOICE_CREATED' : 'SALES.INVOICE_UPDATED');
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: msg,
        });
        this.inlinePayEnabled.set(false);
        // Print after save if requested
        if (this.pendingPrintAfterSave()) {
          this.pendingPrintAfterSave.set(false);
          const invoice = res?.data;
          if (invoice?.id) {
            this.service.getById(invoice.id).subscribe({
              next: (r) => this.printInvoice(r.data),
            });
          }
        }
        this.reloadCurrentPage();
      },
      error: (err) => {
        this.saving.set(false);
        this.pendingPrintAfterSave.set(false);
        this.showError(err);
      },
    });
  }

  onSaveAndPrint(): void {
    this.pendingPrintAfterSave.set(true);
    this.onSave();
  }

  onFiltersChange(filters: Record<string, any>): void {
    this.activeFilters = filters;
    this.first = 0;
    this.loadPage(1);
  }

  onPageChange(event: any): void {
    this.first = event.first;
    this.rows = event.rows;
    const page = Math.floor(event.first / event.rows) + 1;
    this.loadPage(page, event.rows);
  }

  retry(): void {
    this.loadPage();
  }

  getStatusSeverity(
    status: string
  ): 'info' | 'warn' | 'success' | 'danger' | 'secondary' | 'contrast' {
    switch (status) {
      case 'draft':
        return 'secondary';
      case 'pending_approval':
        return 'warn';
      case 'approved':
        return 'success';
      case 'posted':
        return 'success';
      case 'partially_paid':
        return 'info';
      case 'paid':
        return 'success';
      case 'overdue':
        return 'danger';
      case 'cancelled':
        return 'danger';
      case 'partial':
        return 'warn';
      case 'unpaid':
        return 'secondary';
      case 'pending':
        return 'secondary';
      default:
        return 'secondary';
    }
  }

  getStatusLabel(status: string): string {
    const key = 'SALES.' + status.toUpperCase();
    const translated = this.translate.instant(key);
    return translated !== key ? translated : status.replace(/_/g, ' ');
  }

  getDaysInfo(invoice: SalesInvoice): { days: number; overdue: boolean } {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const dueDate = new Date(invoice.due_date);
    dueDate.setHours(0, 0, 0, 0);
    const diff = Math.ceil((dueDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
    return { days: Math.abs(diff), overdue: diff < 0 };
  }

  isOverdue(invoice: SalesInvoice): boolean {
    if (invoice.status === 'cancelled' || invoice.status === 'draft' || invoice.status === 'paid') {
      return false;
    }
    const balance = parseFloat(invoice.balance_due) || 0;
    if (balance <= 0) return false;
    const today = new Date().toISOString().split('T')[0];
    return invoice.due_date < today;
  }

  getPaymentMethodLabel(method: string): string {
    const key = 'SALES.' + method.toUpperCase();
    const translated = this.translate.instant(key);
    return translated !== key ? translated : method.replace(/_/g, ' ');
  }

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

  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) {
          this.addProductByBarcode(result.product);
        } else {
          this.messageService.add({
            severity: 'warn',
            summary: this.translate.instant('COMMON.WARNING'),
            detail: this.translate.instant('INVENTORY.BARCODE_NOT_FOUND'),
          });
        }
        this.barcodeValue.set('');
        this.focusBarcodeInput();
      },
      error: () => {
        this.barcodeLoading.set(false);
        this.barcodeValue.set('');
        this.messageService.add({
          severity: 'warn',
          summary: this.translate.instant('COMMON.WARNING'),
          detail: this.translate.instant('INVENTORY.BARCODE_NOT_FOUND'),
        });
        this.focusBarcodeInput();
      },
    });
  }

  private focusBarcodeInput(): void {
    setTimeout(() => this.barcodeInputRef?.nativeElement?.focus(), 50);
  }

  private addProductByBarcode(product: Product): void {
    // Check if product already exists in items - if so, increment quantity
    for (let i = 0; i < this.items.length; i++) {
      const row = this.items.at(i);
      if (row.get('product_id')?.value === product.id) {
        const currentQty = row.get('quantity')?.value || 0;
        row.patchValue({ quantity: currentQty + 1 });
        return;
      }
    }
    // Ensure product is in searchedProducts so dropdown can display it
    const current = this.searchedProducts();
    if (!current.find((p) => p.id === product.id)) {
      this.searchedProducts.set([...current, product]);
    }
    const priceType = this.invoiceForm.get('price_type')?.value || 'retail';
    const defaultWarehouse = this.invoiceForm.get('warehouse_id')?.value;
    const basePatch = {
      product_id: product.id,
      unit_id: product.base_unit_id || null,
      unit_price: parseFloat(product.sale_price) || 0,
      warehouse_id: defaultWarehouse || null,
      quantity: 1,
    };

    // Helper to set price after loading
    const applyPrice = (row: any) => {
      if (this.productPricesCache.has(product.id)) {
        const cached = this.productPricesCache.get(product.id)!;
        row.patchValue({ unit_price: this.getProductPriceByType(product, priceType, cached) });
      } else {
        this.productService.listPrices(product.id).subscribe({
          next: (res) => {
            const prices = res.data || [];
            this.productPricesCache.set(product.id, prices);
            row.patchValue({ unit_price: this.getProductPriceByType(product, priceType, prices) });
          },
        });
      }
    };

    // If the last row is empty (no product selected), use it instead of adding a new row
    if (this.items.length > 0) {
      const lastRow = this.items.at(this.items.length - 1);
      if (!lastRow.get('product_id')?.value) {
        lastRow.patchValue(basePatch);
        applyPrice(lastRow);
        return;
      }
    }
    // Add a brand new row
    this.items.push(this.createItem(basePatch));
    applyPrice(this.items.at(this.items.length - 1));
  }

  openQuickAddCustomer(): void {
    this.quickAddForm.reset();
    this.quickAddVisible.set(true);
  }

  saveQuickCustomer(): void {
    if (!this.quickAddForm.valid) {
      this.quickAddForm.markAllAsTouched();
      return;
    }
    this.quickAddSaving.set(true);
    const val = this.quickAddForm.value;
    const data: any = {
      name: val.name_ar,
      name_ar: val.name_ar,
      name_en: val.name_en || val.name_ar,
      type: 'individual',
      is_customer: true,
    };
    if (val.phone) data.phone = val.phone;
    if (val.email) data.email = val.email;
    this.partnerService.create(data).subscribe({
      next: (res) => {
        this.quickAddSaving.set(false);
        this.quickAddVisible.set(false);
        const newCustomer = res.data;
        this.searchedCustomers.set([...this.searchedCustomers(), newCustomer]);
        this.invoiceForm.patchValue({ customer_id: newCustomer.id });
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: this.translate.instant('SALES.CUSTOMER_ADDED'),
        });
      },
      error: (err) => {
        this.quickAddSaving.set(false);
        this.messageService.add({
          severity: 'error',
          summary: this.translate.instant('COMMON.ERROR'),
          detail: err.error?.message || 'Failed to create customer',
        });
      },
    });
  }

  onCustomerChange(customerId: number): void {
    if (!customerId) {
      this.customerBalance.set(null);
      return;
    }
    this.customerBalanceLoading.set(true);
    this.customerBalance.set(null);
    // Fetch balance from customer's AR sub-account in chart of accounts (GL)
    // This captures all transactions: invoices, payments, manual JEs, receipt/payment vouchers
    this.partnerService.getById(customerId).subscribe({
      next: (partnerRes) => {
        const arAccountId = partnerRes.data.ar_account_id;
        if (!arAccountId) {
          this.customerBalance.set(0);
          this.customerBalanceLoading.set(false);
          return;
        }
        this.reportService.getGeneralLedger(arAccountId).subscribe({
          next: (glRes) => {
            const entries = glRes.data.entries || [];
            const balance = entries.length > 0
              ? parseFloat(entries[entries.length - 1].running_balance) || 0
              : parseFloat(glRes.data.opening_balance) || 0;
            this.customerBalance.set(balance);
            this.customerBalanceLoading.set(false);
          },
          error: () => {
            this.customerBalance.set(null);
            this.customerBalanceLoading.set(false);
          },
        });
      },
      error: () => {
        this.customerBalance.set(null);
        this.customerBalanceLoading.set(false);
      },
    });
  }

  loadLineStock(index: number, productId: number): void {
    if (!productId) return;
    const warehouseId = this.items.at(index)?.get('warehouse_id')?.value
      || this.invoiceForm.get('warehouse_id')?.value;

    this.stockBalanceService.listFiltered({
      product_id: productId,
      ...(warehouseId ? { warehouse_id: warehouseId } : {}),
    }).subscribe({
      next: (res) => {
        let qty = 0;
        for (const sb of res.data) {
          qty += parseFloat((sb as any).quantity ?? (sb as any).balance ?? '0') || 0;
        }
        const map = new Map(this.lineStockQty());
        map.set(index, qty);
        this.lineStockQty.set(map);
      },
      error: () => {
        const map = new Map(this.lineStockQty());
        map.set(index, null);
        this.lineStockQty.set(map);
      },
    });
  }

  getLineStock(index: number): number | null {
    return this.lineStockQty().get(index) ?? null;
  }

  onDefaultWarehouseChange(warehouseId: number | null): void {
    for (let i = 0; i < this.items.length; i++) {
      const row = this.items.at(i);
      const currentWh = row.get('warehouse_id')?.value;
      if (!currentWh) {
        row.patchValue({ warehouse_id: warehouseId });
      }
    }
  }

  // Cache for loaded product prices (productId → prices[])
  private productPricesCache = new Map<number, import('../../../core/models/product.model').ProductPrice[]>();

  onPriceTypeChange(priceType: string): void {
    const products = this.searchedProducts();
    for (let i = 0; i < this.items.length; i++) {
      const row = this.items.at(i);
      const productId = row.get('product_id')?.value;
      if (!productId) continue;
      const product = products.find((p) => p.id === productId);
      if (!product) continue;
      const cachedPrices = this.productPricesCache.get(productId);
      const newPrice = this.getProductPriceByType(product, priceType, cachedPrices);
      row.patchValue({ unit_price: newPrice });
    }
  }

  private getProductPriceByType(product: Product, priceType: string, cachedPrices?: import('../../../core/models/product.model').ProductPrice[]): number {
    const prices = cachedPrices || product.prices;
    if (prices && prices.length > 0) {
      const match = prices.find((p) => {
        const pt = typeof p.price_type === 'object' ? p.price_type.value : p.price_type;
        return pt === priceType;
      });
      if (match) return parseFloat(match.price) || 0;
    }
    return parseFloat(product.sale_price) || 0;
  }

  onProductSelect(index: number, productId: number): void {
    const product = this.searchedProducts().find((p) => p.id === productId);
    if (!product) return;
    const row = this.items.at(index);
    const priceType = this.invoiceForm.get('price_type')?.value || 'retail';
    const defaultWarehouse = this.invoiceForm.get('warehouse_id')?.value;

    // Set unit and warehouse immediately
    row.patchValue({
      unit_id: product.base_unit_id || null,
      warehouse_id: row.get('warehouse_id')?.value || defaultWarehouse || null,
    });

    // Load prices from API then set the correct price
    if (this.productPricesCache.has(productId)) {
      const cached = this.productPricesCache.get(productId)!;
      row.patchValue({ unit_price: this.getProductPriceByType(product, priceType, cached) });
    } else {
      this.productService.listPrices(productId).subscribe({
        next: (res) => {
          const prices = res.data || [];
          this.productPricesCache.set(productId, prices);
          row.patchValue({ unit_price: this.getProductPriceByType(product, priceType, prices) });
        },
        error: () => {
          row.patchValue({ unit_price: parseFloat(product.sale_price) || 0 });
        },
      });
    }

    // Load stock balance for this product
    this.loadLineStock(index, productId);
  }

  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}`;
  }

  toggleField(field: string): void {
    const current = this.fieldVisibility();
    this.fieldVisibility.set({ ...current, [field]: !current[field] });
  }

  isFieldVisible(field: string): boolean {
    return this.fieldVisibility()[field] !== false;
  }

  saveFieldVisibility(): void {
    const data = { setting_key: 'sales.invoice_visible_fields', value: JSON.stringify(this.fieldVisibility()) };
    this.settingService.update(data).subscribe({
      next: () => {
        this.fieldSettingsVisible.set(false);
        this.messageService.add({
          severity: 'success',
          summary: this.translate.instant('COMMON.SUCCESS'),
          detail: this.translate.instant('SETTINGS.SETTING_UPDATED'),
        });
      },
      error: () => {},
    });
  }

  printInvoice(inv: SalesInvoice): void {
    this.printService.print(INVOICE_PRINT_CONFIG, inv, this.companyInfo());
  }

  private showError(err: any): void {
    this.messageService.add({
      severity: 'error',
      summary: this.translate.instant('COMMON.ERROR'),
      detail: err.error?.message || 'Operation failed',
    });
  }
}
