import { Component, inject, OnInit, OnDestroy, signal } 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, forkJoin } 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 { InventoryCountService } from '../../core/services/inventory-count.service';
import { StockAdjustmentService } from '../../core/services/stock-adjustment.service';
import { SettingService } from '../../core/services/setting.service';
import { ProductService } from '../../core/services/product.service';
import { InventoryCount, StockAdjustment, StockBalance } from '../../core/models/inventory.model';
import { StockBalanceService } from '../../core/services/stock-balance.service';
import { Product, ProductVariant } from '../../core/models/product.model';
import { WarehouseService } from '../../core/services/warehouse.service';
import { Warehouse } from '../../core/models/warehouse.model';
import { selectAllUnits, UnitsActions } from '../../core/store';

@Component({
  selector: 'app-inventory-counts',
  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: './inventory-counts.component.html',
  styleUrl: './inventory-counts.component.scss',
})
export class InventoryCountsComponent implements OnInit, OnDestroy {
  readonly columns: TableColumn[] = [
    { field: 'count_number', header: 'INVENTORY.COUNT_NUMBER' },
    { field: 'date', header: 'INVENTORY.DATE' },
    { field: 'warehouse', header: 'INVENTORY.WAREHOUSE' },
    { field: 'status', header: 'COMMON.STATUS' },
    { field: 'adjustment', header: 'INVENTORY.ADJUSTMENT_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 inventoryCountService = inject(InventoryCountService);
  private stockAdjustmentService = inject(StockAdjustmentService);
  private productService = inject(ProductService);
  private warehouseService = inject(WarehouseService);
  private stockBalanceService = inject(StockBalanceService);
  private settingService = inject(SettingService);

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

  stockQtyMap = signal<Record<string, number>>({});
  items = signal<InventoryCount[]>([]);
  loading = signal(false);
  saving = signal(false);
  editing = signal<InventoryCount | null>(null);
  dialogVisible = signal(false);
  detailDialogVisible = signal(false);
  detailItem = signal<InventoryCount | null>(null);
  warehouses = signal<Warehouse[]>([]);
  searchedProducts = signal<Product[]>([]);
  searchLoading = signal(false);
  products = signal<Product[]>([]);
  productVariantsMap = signal<Record<number, ProductVariant[]>>({});
  private searchSubject = new Subject<string>();

  // Map: count_id → linked adjustment
  adjustmentMap = signal<Record<number, StockAdjustment>>({});

  // Quick mode: save → finalize → approve in one step (from backend setting)
  quickMode = signal(false);
  savingStep = signal(''); // progress label during quick mode save

  form!: FormGroup;

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

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

    this.searchSubject.pipe(
      debounceTime(300),
      filter(q => q.length >= 2),
      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),
    });

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

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

    this.loadData();
  }

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

  loadData(): void {
    this.loading.set(true);
    forkJoin({
      counts: this.inventoryCountService.listAll(),
      adjustments: this.stockAdjustmentService.listAll(),
    }).subscribe({
      next: ({ counts, adjustments }) => {
        this.items.set(counts.data);
        const map: Record<number, StockAdjustment> = {};
        adjustments.data.forEach(adj => {
          if ((adj as any).inventory_count_id) {
            map[(adj as any).inventory_count_id] = adj;
          }
        });
        this.adjustmentMap.set(map);
        this.loading.set(false);
      },
      error: (err) => { this.loading.set(false); this.showError(err); },
    });
  }

  getLinkedAdjustment(countId: number): StockAdjustment | null {
    return this.adjustmentMap()[countId] || null;
  }

  openNew(): void {
    this.editing.set(null);
    this.form.reset({ date: new Date() });
    this.itemsArray.clear();
    this.searchedProducts.set([]);
    this.products.set([]);
    this.addItem();
    this.dialogVisible.set(true);
  }

  edit(item: InventoryCount): void {
    this.editing.set(item);
    this.form.patchValue({
      warehouse_id: item.warehouse_id,
      date: new Date(item.date),
      notes: item.notes,
    });
    this.itemsArray.clear();
    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: null,
        } as any as Product));
      this.searchedProducts.set(existingProducts);
      this.products.set(existingProducts);

      item.items.forEach(line => {
        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],
          counted_quantity: [line.counted_quantity, [Validators.required, Validators.min(0)]],
          unit_cost: [line.unit_cost, [Validators.required, Validators.min(0)]],
          notes: [line.notes || ''],
        }));
        if (line.product_variant_id) {
          this.onProductChange(this.itemsArray.length - 1, line.product_id);
        }
      });
    } else {
      this.addItem();
    }
    this.dialogVisible.set(true);
  }

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

  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),
      notes: val.notes || null,
      items: val.items.map((item: any) => ({
        product_id: item.product_id,
        product_variant_id: item.product_variant_id || null,
        unit_id: item.unit_id,
        counted_quantity: item.counted_quantity,
        unit_cost: item.unit_cost,
        notes: item.notes || null,
      })),
    };

    if (this.editing()) {
      // Edit mode — just update, no auto-chain
      this.inventoryCountService.update(this.editing()!.id, 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.COUNT_UPDATE_SUCCESS') });
          this.loadData();
        },
        error: (err) => { this.saving.set(false); this.savingStep.set(''); this.showError(err); },
      });
      return;
    }

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

  /** Normal mode: just create the count as draft */
  private normalSave(data: any): void {
    this.savingStep.set(this.translate.instant('INVENTORY.STEP_CREATING'));
    this.inventoryCountService.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.COUNT_CREATE_SUCCESS') });
        this.loadData();
      },
      error: (err) => { this.saving.set(false); this.savingStep.set(''); this.showError(err); },
    });
  }

  /** Quick mode: create → finalize → approve adjustment — all chained */
  private quickSave(data: any): void {
    // Step 1: Create
    this.savingStep.set(this.translate.instant('INVENTORY.STEP_CREATING'));
    this.inventoryCountService.create(data).subscribe({
      next: (createRes) => {
        const countId = createRes.data.id;
        // Step 2: Finalize
        this.savingStep.set(this.translate.instant('INVENTORY.STEP_FINALIZING'));
        this.inventoryCountService.approve(countId).subscribe({
          next: () => {
            // Step 3: Load adjustments to find the auto-created one
            this.savingStep.set(this.translate.instant('INVENTORY.STEP_CONFIRMING'));
            this.stockAdjustmentService.listAll().subscribe({
              next: (adjRes) => {
                const linkedAdj = adjRes.data.find((a: any) => a.inventory_count_id === countId);
                if (linkedAdj && linkedAdj.status === 'draft') {
                  // Step 4: Approve adjustment
                  this.stockAdjustmentService.approve(linkedAdj.id).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_COUNT_SUCCESS') });
                      this.loadData();
                    },
                    error: (err) => {
                      // Adjustment approve failed, but count is finalized
                      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_COUNT_PARTIAL') });
                      this.loadData();
                    },
                  });
                } else {
                  // No draft adjustment found — count finalized but no adj to approve
                  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.COUNT_APPROVE_SUCCESS') });
                  this.loadData();
                }
              },
              error: () => {
                this.saving.set(false);
                this.savingStep.set('');
                this.dialogVisible.set(false);
                this.messageService.add({ severity: 'warn', summary: this.translate.instant('COMMON.WARNING'), detail: this.translate.instant('INVENTORY.QUICK_COUNT_PARTIAL') });
                this.loadData();
              },
            });
          },
          error: (err) => {
            // Finalize failed — count 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_COUNT_PARTIAL') });
            this.loadData();
          },
        });
      },
      error: (err) => { this.saving.set(false); this.savingStep.set(''); this.showError(err); },
    });
  }

  delete(item: InventoryCount): void {
    this.confirmationService.confirm({
      message: this.translate.instant('INVENTORY.CONFIRM_DELETE_COUNT'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-exclamation-triangle',
      acceptButtonStyleClass: 'p-button-danger',
      accept: () => {
        this.saving.set(true);
        this.inventoryCountService.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.COUNT_DELETE_SUCCESS') });
          },
          error: (err) => { this.saving.set(false); this.showError(err); },
        });
      },
    });
  }

  /** Step 1 (complete mode): Finalize the count */
  finalize(item: InventoryCount): void {
    this.confirmationService.confirm({
      message: this.translate.instant('INVENTORY.CONFIRM_APPROVE_COUNT'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-check-circle',
      accept: () => {
        this.saving.set(true);
        this.inventoryCountService.approve(item.id).subscribe({
          next: () => {
            this.saving.set(false);
            this.messageService.add({ severity: 'success', summary: this.translate.instant('COMMON.SUCCESS'), detail: this.translate.instant('INVENTORY.COUNT_APPROVE_SUCCESS') });
            this.loadData();
          },
          error: (err) => { this.saving.set(false); this.showError(err); },
        });
      },
    });
  }

  /** Step 2 (complete mode): Confirm the linked adjustment */
  confirmAdjustment(item: InventoryCount): void {
    const adj = this.getLinkedAdjustment(item.id);
    if (!adj) return;
    this.confirmationService.confirm({
      message: this.translate.instant('INVENTORY.CONFIRM_APPROVE_ADJUSTMENT'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-check-circle',
      accept: () => {
        this.saving.set(true);
        this.stockAdjustmentService.approve(adj.id).subscribe({
          next: () => {
            this.saving.set(false);
            this.messageService.add({ severity: 'success', summary: this.translate.instant('COMMON.SUCCESS'), detail: this.translate.instant('INVENTORY.ADJUSTMENT_APPROVE_SUCCESS') });
            this.loadData();
          },
          error: (err) => { this.saving.set(false); 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],
      counted_quantity: [0, [Validators.required, Validators.min(0)]],
      unit_cost: [0, [Validators.required, Validators.min(0)]],
      notes: [''],
    }));
  }

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

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

  onProductChange(index: number, productId: number): void {
    const product = this.products().find(p => p.id === productId);
    if (product?.has_variants && !this.productVariantsMap()[productId]) {
      this.productService.listVariants(productId).subscribe({
        next: (res) => {
          this.productVariantsMap.update(m => ({ ...m, [productId]: res.data }));
        },
      });
    }
    const group = this.itemsArray.at(index) as FormGroup;
    group.patchValue({ product_variant_id: null });
    if (product?.base_unit_id) {
      group.patchValue({ unit_id: product.base_unit_id });
    } else if (product) {
      this.productService.listProductUnits(product.id).subscribe({
        next: (res) => {
          const firstUnit = res.data?.[0]?.unit_id || null;
          if (firstUnit) group.patchValue({ unit_id: firstUnit });
        },
      });
    }
    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;
  }

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

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

  getAdjStatusSeverity(adj: StockAdjustment | null): 'success' | 'warn' | 'danger' | 'info' | 'secondary' {
    if (!adj) return 'secondary';
    switch (adj.status) {
      case 'approved': return 'success';
      case 'cancelled': return 'danger';
      default: return 'warn';
    }
  }

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

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