import { Component, inject, OnInit, OnDestroy, signal, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button';
import { DialogModule } from 'primeng/dialog';
import { TagModule } from 'primeng/tag';
import { ToastModule } from 'primeng/toast';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { TooltipModule } from 'primeng/tooltip';
import { SelectModule } from 'primeng/select';
import { InputNumberModule } from 'primeng/inputnumber';
import { InputTextModule } from 'primeng/inputtext';
import { DatePickerModule } from 'primeng/datepicker';
import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { MessageService, ConfirmationService } from 'primeng/api';
import {
  PageHeaderComponent,
  DataTableComponent,
  TableColumn,
  SalesFilterBarComponent,
} from '../../../shared';
import { PurchasePayment } from '../../../core/models/purchase-payment.model';
import { PurchasePaymentService } from '../../../core/services/purchase-payment.service';
import { PartnerService } from '../../../core/services/partner.service';
import { SettingService } from '../../../core/services/setting.service';
import { PettyCashService } from '../../../core/services/petty-cash.service';
import { BankAccountService } from '../../../core/services/bank-account.service';
import { ReportService } from '../../../core/services/report.service';
import { VoucherService } from '../../../core/services/voucher.service';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../../../environments/environment';

interface OutstandingBill {
  id: number;
  bill_number: string;
  date: string;
  due_date: string;
  total: number;
  amount_paid: number;
  balance_due: number;
  allocated: number;
}

@Component({
  selector: 'app-purchase-payments',
  standalone: true,
  imports: [
    CommonModule, FormsModule, TranslateModule, TableModule, ButtonModule, DialogModule,
    TagModule, ToastModule, ConfirmDialogModule, TooltipModule, SelectModule,
    InputNumberModule, InputTextModule, DatePickerModule, ProgressSpinnerModule,
    PageHeaderComponent, DataTableComponent, SalesFilterBarComponent,
  ],
  providers: [MessageService, ConfirmationService],
  templateUrl: './payments.component.html',
  styleUrl: './payments.component.scss',
})
export class PurchasePaymentsComponent implements OnInit, OnDestroy {
  private messageService = inject(MessageService);
  private confirmationService = inject(ConfirmationService);
  private translate = inject(TranslateService);
  private service = inject(PurchasePaymentService);
  private partnerService = inject(PartnerService);
  private settingService = inject(SettingService);
  private pettyCashService = inject(PettyCashService);
  private bankAccountService = inject(BankAccountService);
  private reportService = inject(ReportService);
  private voucherService = inject(VoucherService);
  private http = inject(HttpClient);
  private langSub!: Subscription;

  readonly columns: TableColumn[] = [
    { field: 'payment_number', header: 'PURCHASES.PAYMENT_NUMBER' },
    { field: 'date', header: 'PURCHASES.DATE', sortable: true },
    { field: 'bill', header: 'PURCHASES.BILL_NUMBER' },
    { field: 'partner', header: 'PURCHASES.SUPPLIER' },
    { field: 'amount', header: 'PURCHASES.AMOUNT', sortable: true },
    { field: 'payment_method', header: 'PURCHASES.PAYMENT_METHOD' },
    { field: 'status', header: 'PURCHASES.STATUS' },
    { field: 'actions', header: 'COMMON.ACTIONS' },
  ];

  tableData = signal<PurchasePayment[]>([]);
  totalRecords = signal(0);
  tableLoading = signal(false);
  first = 0;
  rows = 25;
  activeFilters: Record<string, any> = {};
  filterSuppliers = signal<any[]>([]);

  readonly statusOptions = [
    { label: 'مسودة', value: 'draft' },
    { label: 'مرحّل', value: 'posted' },
    { label: 'ملغي', value: 'cancelled' },
  ];

  viewDialogVisible = signal(false);
  viewingPayment = signal<PurchasePayment | null>(null);

  // Settlement dialog
  settleDialogVisible = signal(false);
  settleLoading = signal(false);
  settleSaving = signal(false);
  settleDate = new Date();
  settleSupplierId = signal<number | null>(null);
  settleAmount = signal<number>(0);
  settlePaymentMethod = signal<string>('cash');
  settleAccountId = signal<number | null>(null);
  settleNotes = signal<string>('');
  settleBankAccountId = signal<number | null>(null);
  partnerBalance = signal<number | null>(null);
  partnerBalanceLoading = signal(false);
  settleFromBalance = signal(false);
  selectedPartnerApAccountId = signal<number | null>(null);
  suppliers = signal<any[]>([]);
  outstandingBills = signal<OutstandingBill[]>([]);
  defaultCashAccount = signal<number | null>(null);
  defaultBankAccount = signal<number | null>(null);
  defaultCheckAccount = signal<number | null>(null);
  paymentAccountOptions = signal<{ label: string; value: number; group: string; bankAccountId?: number }[]>([]);
  private allPaymentOptions: { label: string; value: number; group: string; bankAccountId?: number }[] = [];

  totalOutstanding = computed(() =>
    this.outstandingBills().reduce((s, b) => s + b.balance_due, 0)
  );
  totalAllocated = computed(() =>
    this.outstandingBills().reduce((s, b) => s + b.allocated, 0)
  );
  remainingAmount = computed(() => this.settleAmount() - this.totalAllocated());

  readonly paymentMethods = [
    { label: 'VOUCHERS.CASH', value: 'cash' },
    { label: 'VOUCHERS.BANK_TRANSFER', value: 'bank_transfer' },
    { label: 'VOUCHERS.CHECK', value: 'check' },
  ];

  ngOnInit(): void {
    this.langSub = this.translate.onLangChange.subscribe(() => {});
    this.loadPage();

    this.partnerService.searchForDropdown('', { is_supplier: '1' }).subscribe({
      next: (res) => {
        this.filterSuppliers.set(res.data);
        this.suppliers.set(res.data);
      },
      error: () => {},
    });

    // Load petty cash + bank accounts (like vouchers)
    this.loadPaymentAccountOptions();

    this.settingService.getByKey('accounting.default_cash_account').subscribe({
      next: (res: any) => {
        const v = res?.data?.value ?? res?.value;
        if (v && v !== '0') this.defaultCashAccount.set(Number(v));
      },
    });
    this.settingService.getByKey('accounting.default_bank_account').subscribe({
      next: (res: any) => {
        const v = res?.data?.value ?? res?.value;
        if (v && v !== '0') this.defaultBankAccount.set(Number(v));
      },
    });
    this.settingService.getByKey('accounting.default_check_account').subscribe({
      next: (res: any) => {
        const v = res?.data?.value ?? res?.value;
        if (v && v !== '0') this.defaultCheckAccount.set(Number(v));
      },
    });
  }

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

  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); },
      error: () => this.tableLoading.set(false),
    });
  }

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

  viewPayment(pay: PurchasePayment): void {
    this.service.getById(pay.id).subscribe({
      next: (res) => { this.viewingPayment.set(res.data); this.viewDialogVisible.set(true); },
      error: () => { this.viewingPayment.set(pay); this.viewDialogVisible.set(true); },
    });
  }

  postPayment(pay: PurchasePayment): void {
    this.confirmationService.confirm({
      message: this.translate.instant('COMMON.CONFIRM_ACTION'),
      header: this.translate.instant('COMMON.CONFIRM'),
      icon: 'pi pi-exclamation-triangle',
      accept: () => {
        this.service.post(pay.id).subscribe({
          next: () => { this.messageService.add({ severity: 'success', summary: this.translate.instant('COMMON.SUCCESS'), detail: this.translate.instant('PURCHASES.PAYMENT_POSTED') }); this.reloadCurrentPage(); },
          error: (err) => this.showError(err),
        });
      },
    });
  }

  cancelPayment(pay: PurchasePayment): 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(pay.id).subscribe({
          next: () => { this.messageService.add({ severity: 'success', summary: this.translate.instant('COMMON.SUCCESS'), detail: this.translate.instant('PURCHASES.PAYMENT_CANCELLED') }); this.reloadCurrentPage(); },
          error: (err) => this.showError(err),
        });
      },
    });
  }

  deletePayment(pay: PurchasePayment): void {
    if (pay.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(pay.id).subscribe({
          next: () => { this.messageService.add({ severity: 'success', summary: this.translate.instant('COMMON.SUCCESS'), detail: this.translate.instant('PURCHASES.PAYMENT_DELETED') }); this.reloadCurrentPage(); },
          error: (err) => this.showError(err),
        });
      },
    });
  }

  onFiltersChange(filters: Record<string, any>): void {
    if (filters['customer_id']) { filters['supplier_id'] = filters['customer_id']; delete filters['customer_id']; }
    this.activeFilters = filters; this.first = 0; this.loadPage(1);
  }

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

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

  // ─── Settlement ────────────────────────────────────────────────

  openSettleDialog(): void {
    this.settleDate = new Date();
    this.settleSupplierId.set(null);
    this.settleAmount.set(0);
    this.settlePaymentMethod.set('cash');
    this.settleNotes.set('');
    this.outstandingBills.set([]);
    this.partnerBalance.set(null);
    this.settleFromBalance.set(false);
    this.selectedPartnerApAccountId.set(null);
    this.filterPaymentOptions();
    const opts = this.paymentAccountOptions();
    const defaultId = this.defaultCashAccount();
    if (defaultId && opts.some(o => o.value === defaultId)) {
      this.settleAccountId.set(defaultId);
      this.syncBankAccountId(defaultId);
    } else if (opts.length > 0) {
      this.settleAccountId.set(opts[0].value);
      this.syncBankAccountId(opts[0].value);
    } else {
      this.settleAccountId.set(null);
      this.settleBankAccountId.set(null);
    }
    this.settleDialogVisible.set(true);
  }

  onSettleSupplierChange(supplierId: number | null): void {
    this.settleSupplierId.set(supplierId);
    this.outstandingBills.set([]);
    this.partnerBalance.set(null);
    this.settleFromBalance.set(false);
    this.selectedPartnerApAccountId.set(null);
    if (!supplierId) return;

    // Load partner balance + AP account
    this.partnerBalanceLoading.set(true);
    const now = new Date();
    const endDate = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
    this.reportService.getPartnerStatement(supplierId, '2000-01-01', endDate).subscribe({
      next: (res) => { this.partnerBalance.set(+(res.data?.closing_balance || 0)); this.partnerBalanceLoading.set(false); },
      error: () => this.partnerBalanceLoading.set(false),
    });
    // Get partner's AP account ID
    this.partnerService.getById(supplierId).subscribe({
      next: (res: any) => { this.selectedPartnerApAccountId.set(res?.data?.ap_account_id || null); },
      error: () => {},
    });

    this.settleLoading.set(true);
    this.http.get<any>(`${environment.apiUrl}/purchases/bills`, {
      params: { supplier_id: supplierId, per_page: '100' },
    }).subscribe({
      next: (res) => {
        const bills: OutstandingBill[] = (res.data || [])
          .filter((b: any) => parseFloat(b.balance_due) > 0 && ['posted', 'partially_paid'].includes(b.status))
          .sort((a: any, b: any) => a.date.localeCompare(b.date))
          .map((b: any) => ({
            id: b.id,
            bill_number: b.bill_number,
            date: b.date,
            due_date: b.due_date || b.date,
            total: parseFloat(b.total),
            amount_paid: parseFloat(b.amount_paid),
            balance_due: parseFloat(b.balance_due),
            allocated: 0,
          }));
        this.outstandingBills.set(bills);
        this.settleLoading.set(false);
      },
      error: () => this.settleLoading.set(false),
    });
  }

  onSettleAmountChange(amount: number): void {
    this.settleAmount.set(amount || 0);
    this.autoAllocate();
  }

  onSettleMethodChange(method: string): void {
    this.settlePaymentMethod.set(method);
    this.filterPaymentOptions();
    const map: Record<string, number | null> = { cash: this.defaultCashAccount(), bank_transfer: this.defaultBankAccount(), check: this.defaultCheckAccount() };
    const defaultId = map[method] ?? null;
    const opts = this.paymentAccountOptions();
    if (defaultId && opts.some(o => o.value === defaultId)) {
      this.settleAccountId.set(defaultId);
      this.syncBankAccountId(defaultId);
    } else if (opts.length > 0) {
      this.settleAccountId.set(opts[0].value);
      this.syncBankAccountId(opts[0].value);
    } else {
      this.settleAccountId.set(null);
      this.settleBankAccountId.set(null);
    }
  }

  onSettleAccountChange(accountId: number): void {
    this.settleAccountId.set(accountId);
    this.syncBankAccountId(accountId);
  }

  private syncBankAccountId(accountId: number): void {
    const opt = this.allPaymentOptions.find(o => o.value === accountId);
    this.settleBankAccountId.set(opt?.bankAccountId || null);
  }

  toggleSettleFromBalance(checked: boolean): void {
    this.settleFromBalance.set(checked);
    if (checked) {
      // balance is the CURRENT GL balance (after bill postings already credited AP)
      // Pre-bill credit = balance - totalOutstanding (if negative, supplier had credit)
      const balance = this.partnerBalance() || 0;
      const preBillBalance = balance - this.totalOutstanding();
      const preBillCredit = preBillBalance < 0 ? Math.abs(preBillBalance) : 0;
      // Can only settle up to the original credit amount
      const maxFromBalance = Math.min(preBillCredit, this.totalOutstanding());
      if (maxFromBalance > 0) {
        this.settleAmount.set(parseFloat(maxFromBalance.toFixed(3)));
        this.autoAllocate();
      }
    }
  }

  autoAllocate(): void {
    let remaining = this.settleAmount();
    const bills = this.outstandingBills().map(b => {
      const alloc = Math.min(remaining, b.balance_due);
      remaining -= alloc;
      return { ...b, allocated: parseFloat(alloc.toFixed(3)) };
    });
    this.outstandingBills.set(bills);
  }

  onAllocationChange(index: number, value: number): void {
    const bills = [...this.outstandingBills()];
    bills[index] = { ...bills[index], allocated: Math.min(value || 0, bills[index].balance_due) };
    this.outstandingBills.set(bills);
  }

  saveSettlement(): void {
    const allocated = this.outstandingBills().filter(b => b.allocated > 0);
    const excessAmount = this.remainingAmount();
    if (allocated.length === 0 && excessAmount <= 0) return;

    const supplierId = this.settleSupplierId();
    const fromBalance = this.settleFromBalance();
    const accountId = fromBalance ? this.selectedPartnerApAccountId() : this.settleAccountId();
    if (!supplierId || !accountId) return;

    this.settleSaving.set(true);
    const dateStr = this.formatDate(this.settleDate);

    let completed = 0;
    let errors = 0;
    const total = allocated.length;
    const excess = parseFloat(excessAmount.toFixed(2));

    const onAllDone = () => {
      if (excess > 0 && !fromBalance) {
        // Create payment voucher for excess amount → adds to supplier credit
        this.voucherService.create('payment', {
          date: dateStr,
          partner_id: supplierId,
          total_amount: excess,
          payment_method: this.settlePaymentMethod(),
          paying_account_id: accountId,
          bank_account_id: this.settleBankAccountId() || undefined,
          description: this.settleNotes() || this.translate.instant('PURCHASES.ADVANCE_PAYMENT'),
        }).subscribe({
          next: (res) => {
            this.voucherService.approve('payment', res.data.id).subscribe({
              next: () => this.finishSettlement(completed, errors, total, true),
              error: () => this.finishSettlement(completed, errors, total, true),
            });
          },
          error: () => this.finishSettlement(completed, errors + 1, total, true),
        });
      } else {
        this.finishSettlement(completed, errors, total, false);
      }
    };

    if (allocated.length === 0) {
      onAllDone();
      return;
    }

    const processNext = (idx: number) => {
      if (idx >= total) {
        onAllDone();
        return;
      }

      const bill = allocated[idx];
      this.service.create({
        date: dateStr,
        bill_id: bill.id,
        partner_id: supplierId,
        amount: bill.allocated,
        payment_method: fromBalance ? 'cash' : this.settlePaymentMethod() as any,
        paying_account_id: accountId,
        bank_account_id: fromBalance ? undefined : (this.settleBankAccountId() || undefined),
        notes: this.settleNotes() || (fromBalance ? this.translate.instant('PURCHASES.SETTLE_FROM_BALANCE') : undefined),
      }).subscribe({
        next: (res) => {
          this.service.post(res.data.id).subscribe({
            next: () => { completed++; processNext(idx + 1); },
            error: () => { completed++; processNext(idx + 1); },
          });
        },
        error: () => { errors++; processNext(idx + 1); },
      });
    };

    processNext(0);
  }

  private finishSettlement(completed: number, errors: number, total: number, hadExcess: boolean): void {
    this.settleSaving.set(false);
    if (errors > 0) {
      this.messageService.add({ severity: 'warn', summary: this.translate.instant('COMMON.WARNING'), detail: `${completed}/${total + (hadExcess ? 1 : 0)} OK, ${errors} failed` });
    } else {
      this.messageService.add({ severity: 'success', summary: this.translate.instant('COMMON.SUCCESS'), detail: this.translate.instant('PURCHASES.SETTLEMENT_SUCCESS', { count: completed + (hadExcess ? 1 : 0) }) });
    }
    this.settleDialogVisible.set(false);
    this.reloadCurrentPage();
  }

  private loadPaymentAccountOptions(): void {
    const options: { label: string; value: number; group: string; bankAccountId?: number }[] = [];
    let loaded = 0;
    const done = () => {
      loaded++;
      if (loaded === 2) {
        this.allPaymentOptions = options;
        this.filterPaymentOptions();
      }
    };
    this.pettyCashService.listAll().subscribe({
      next: (res) => { res.data.filter((pc: any) => pc.is_active).forEach((pc: any) => options.push({ label: pc.name, value: pc.account_id, group: 'cash' })); done(); },
      error: () => done(),
    });
    this.bankAccountService.listAll().subscribe({
      next: (res) => { res.data.filter((ba: any) => ba.is_active).forEach((ba: any) => options.push({ label: ba.bank_name, value: ba.account_id, group: 'bank', bankAccountId: ba.id })); done(); },
      error: () => done(),
    });
  }

  private filterPaymentOptions(): void {
    const method = this.settlePaymentMethod();
    if (method === 'cash') {
      this.paymentAccountOptions.set(this.allPaymentOptions.filter(o => o.group === 'cash'));
    } else if (method === 'bank_transfer') {
      this.paymentAccountOptions.set(this.allPaymentOptions.filter(o => o.group === 'bank'));
    } else {
      this.paymentAccountOptions.set(this.allPaymentOptions);
    }
  }

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

  // ─── Helpers ────────────────────────────────────────────────

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

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

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

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