import { Component, inject, OnInit, signal, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { forkJoin } from 'rxjs';
import { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button';
import { SelectModule } from 'primeng/select';
import { MultiSelectModule } from 'primeng/multiselect';
import { DatePickerModule } from 'primeng/datepicker';
import { TagModule } from 'primeng/tag';
import { PanelModule } from 'primeng/panel';
import { TooltipModule } from 'primeng/tooltip';
import { DialogModule } from 'primeng/dialog';
import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { PageHeaderComponent } from '../../shared';
import { LedgerService, LedgerLine } from '../../core/services/ledger.service';
import { PettyCashService } from '../../core/services/petty-cash.service';
import { BankAccountService } from '../../core/services/bank-account.service';
import { CheckReceivedService } from '../../core/services/check-received.service';
import { CheckIssuedService } from '../../core/services/check-issued.service';
import { JournalEntryService } from '../../core/services/journal-entry.service';
import { VoucherService } from '../../core/services/voucher.service';
import { SalesInvoiceService } from '../../core/services/sales-invoice.service';
import { SalesPaymentService } from '../../core/services/sales-payment.service';
import { SalesReturnService } from '../../core/services/sales-return.service';
import { ExportService, ExportColumn } from '../../core/services/export.service';

interface CashAccount {
  id: number;
  accountId: number;
  name: string;
  type: 'petty_cash' | 'bank';
}

interface AccountLedgerData {
  account: CashAccount;
  openingBalance: number;
  entries: LedgerLine[];
  closingBalance: number;
  totalDebit: number;
  totalCredit: number;
}

interface SourceInfo {
  label: string;
  severity: 'info' | 'warn' | 'success' | 'danger' | 'secondary' | 'contrast';
  route: string | null;
}

@Component({
  selector: 'app-cash-movement',
  standalone: true,
  imports: [
    CommonModule, FormsModule, TranslateModule,
    TableModule, ButtonModule, SelectModule, MultiSelectModule,
    DatePickerModule, TagModule, PanelModule, TooltipModule,
    DialogModule, ProgressSpinnerModule, PageHeaderComponent,
  ],
  templateUrl: './cash-movement.component.html',
  styleUrl: './cash-movement.component.scss',
})
export class CashMovementComponent implements OnInit {
  private router = inject(Router);
  private translate = inject(TranslateService);
  private ledgerService = inject(LedgerService);
  private pettyCashService = inject(PettyCashService);
  private bankAccountService = inject(BankAccountService);
  private checkReceivedService = inject(CheckReceivedService);
  private checkIssuedService = inject(CheckIssuedService);
  private journalEntryService = inject(JournalEntryService);
  private voucherService = inject(VoucherService);
  private salesInvoiceService = inject(SalesInvoiceService);
  private salesPaymentService = inject(SalesPaymentService);
  private salesReturnService = inject(SalesReturnService);
  private exportService = inject(ExportService);

  // Account options
  allAccounts = signal<CashAccount[]>([]);
  accountTypeOptions: { label: string; value: string }[] = [];
  selectedTypes = signal<string[]>(['petty_cash', 'bank']);
  selectedAccountId = signal<number | null>(null);

  // Date filters
  dateFrom = signal<Date>(new Date(new Date().getFullYear(), new Date().getMonth(), 1));
  dateTo = signal<Date>(new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0));

  // Data
  loading = signal(false);
  loaded = signal(false);
  accountLedgers = signal<AccountLedgerData[]>([]);
  checksReceived = signal<any[]>([]);
  checksIssued = signal<any[]>([]);
  showChecksReceived = signal(false);
  showChecksIssued = signal(false);

  // JE detail dialog
  jeDialogVisible = signal(false);
  jeLoading = signal(false);
  jeDetail = signal<any>(null);
  private jeCache: Map<number, any> = new Map();

  // Computed summaries
  totalOpening = computed(() => this.accountLedgers().reduce((s, a) => s + a.openingBalance, 0));
  totalInflows = computed(() => this.accountLedgers().reduce((s, a) => s + a.totalDebit, 0));
  totalOutflows = computed(() => this.accountLedgers().reduce((s, a) => s + a.totalCredit, 0));
  totalClosing = computed(() => this.accountLedgers().reduce((s, a) => s + a.closingBalance, 0));

  filteredAccounts = computed(() => {
    const types = this.selectedTypes();
    return this.allAccounts().filter(a => types.includes(a.type));
  });

  ngOnInit(): void {
    this.accountTypeOptions = [
      { label: this.translate.instant('CASH_MOVEMENT.TYPE_PETTY_CASH'), value: 'petty_cash' },
      { label: this.translate.instant('CASH_MOVEMENT.TYPE_BANK'), value: 'bank' },
    ];
    this.loadAccounts();
  }

  private loadAccounts(): void {
    const accounts: CashAccount[] = [];
    let loaded = 0;
    const done = () => {
      loaded++;
      if (loaded === 2) this.allAccounts.set(accounts);
    };

    this.pettyCashService.listAll().subscribe({
      next: (res) => {
        res.data.filter(pc => pc.is_active).forEach(pc =>
          accounts.push({ id: pc.id, accountId: pc.account_id, name: pc.name, type: 'petty_cash' })
        );
        done();
      },
      error: () => done(),
    });
    this.bankAccountService.listAll().subscribe({
      next: (res) => {
        res.data.filter(ba => ba.is_active).forEach(ba =>
          accounts.push({ id: ba.id, accountId: ba.account_id, name: ba.bank_name, type: 'bank' })
        );
        done();
      },
      error: () => done(),
    });
  }

  setToday(): void {
    const today = new Date();
    this.dateFrom.set(today);
    this.dateTo.set(today);
  }

  setThisMonth(): void {
    const now = new Date();
    this.dateFrom.set(new Date(now.getFullYear(), now.getMonth(), 1));
    this.dateTo.set(new Date(now.getFullYear(), now.getMonth() + 1, 0));
  }

  setThisYear(): void {
    const now = new Date();
    this.dateFrom.set(new Date(now.getFullYear(), 0, 1));
    this.dateTo.set(new Date(now.getFullYear(), 11, 31));
  }

  generate(): void {
    const from = this.formatDate(this.dateFrom());
    const to = this.formatDate(this.dateTo());
    const types = this.selectedTypes();
    const selectedId = this.selectedAccountId();

    let accounts = this.allAccounts().filter(a => types.includes(a.type));
    if (selectedId) accounts = accounts.filter(a => a.accountId === selectedId);

    if (accounts.length === 0 && !types.includes('checks_received') && !types.includes('checks_issued')) return;

    this.loading.set(true);
    this.loaded.set(false);
    this.jeCache.clear();

    // Fetch account ledgers
    const ledgerRequests = accounts.map(acc =>
      this.ledgerService.accountLedger(acc.accountId, from, to)
    );

    if (ledgerRequests.length > 0) {
      forkJoin(ledgerRequests).subscribe({
        next: (results) => {
          const ledgers: AccountLedgerData[] = results.map((res, i) => {
            const entries = res.data.entries || [];
            const opening = +(res.data.opening_balance || 0);
            const totalDebit = entries.reduce((s: number, e: LedgerLine) => s + (+e.debit || 0), 0);
            const totalCredit = entries.reduce((s: number, e: LedgerLine) => s + (+e.credit || 0), 0);
            return {
              account: accounts[i],
              openingBalance: opening,
              entries,
              closingBalance: opening + totalDebit - totalCredit,
              totalDebit,
              totalCredit,
            };
          });
          this.accountLedgers.set(ledgers.filter(l => l.entries.length > 0 || l.openingBalance !== 0));
          this.loading.set(false);
          this.loaded.set(true);
        },
        error: () => {
          this.loading.set(false);
        },
      });
    } else {
      this.accountLedgers.set([]);
      this.loading.set(false);
      this.loaded.set(true);
    }
  }

  /** Extract source document ref from description (e.g. "Payment received - Invoice INV-2026-00004") */
  extractDocRef(entry: LedgerLine): { prefix: string; ref: string } | null {
    const desc = entry.description || entry.entry_description || '';
    const match = desc.match(/(INV|SR|SPAY|RV|PV|TR)-\d{4}-\d+/);
    if (match) return { prefix: match[0].split('-')[0], ref: match[0] };
    return null;
  }

  private sourceInfoByPrefix(prefix: string): SourceInfo {
    switch (prefix) {
      case 'INV': return { label: this.translate.instant('CASH_MOVEMENT.SRC_INVOICE'), severity: 'info', route: '/sales/invoices' };
      case 'SR': return { label: this.translate.instant('CASH_MOVEMENT.SRC_RETURN'), severity: 'warn', route: '/sales/returns' };
      case 'SPAY': return { label: this.translate.instant('CASH_MOVEMENT.SRC_PAYMENT'), severity: 'success', route: '/sales/payments' };
      case 'RV': return { label: this.translate.instant('CASH_MOVEMENT.SRC_RECEIPT_VOUCHER'), severity: 'success', route: '/accounting/receipt-vouchers' };
      case 'PV': return { label: this.translate.instant('CASH_MOVEMENT.SRC_PAYMENT_VOUCHER'), severity: 'danger', route: '/accounting/payment-vouchers' };
      case 'JV': return { label: this.translate.instant('CASH_MOVEMENT.SRC_JOURNAL'), severity: 'contrast', route: '/accounting/journal-entries' };
      case 'TR': return { label: this.translate.instant('CASH_MOVEMENT.SRC_TRANSFER'), severity: 'secondary', route: '/accounting/transfers' };
      default: return { label: '-', severity: 'secondary', route: null };
    }
  }

  getSourceInfo(entry: LedgerLine): SourceInfo {
    const doc = this.extractDocRef(entry);
    if (doc) return this.sourceInfoByPrefix(doc.prefix);
    // Fallback: check entry_number prefix
    const prefix = entry.entry_number?.split('-')[0]?.toUpperCase();
    if (prefix && prefix !== 'JV') return this.sourceInfoByPrefix(prefix);
    return { label: this.translate.instant('CASH_MOVEMENT.SRC_JOURNAL'), severity: 'contrast', route: '/accounting/journal-entries' };
  }

  getDocNumber(entry: LedgerLine): string {
    const doc = this.extractDocRef(entry);
    return doc ? doc.ref : (entry.entry_number || '-');
  }

  navigateToSource(entry: LedgerLine): void {
    const doc = this.extractDocRef(entry);
    const prefix = doc?.prefix || entry.entry_number?.split('-')[0]?.toUpperCase();
    const ref = doc?.ref || entry.entry_number;
    const info = this.getSourceInfo(entry);
    if (!info.route) return;

    switch (prefix) {
      case 'RV':
        this.voucherService.listAll('receipt').subscribe({
          next: (res) => {
            const v = res.data.find(v => v.voucher_number === ref);
            this.router.navigate([info.route!], v ? { queryParams: { viewId: v.id } } : {});
          },
          error: () => this.router.navigate([info.route!]),
        });
        break;
      case 'PV':
        this.voucherService.listAll('payment').subscribe({
          next: (res) => {
            const v = res.data.find(v => v.voucher_number === ref);
            this.router.navigate([info.route!], v ? { queryParams: { viewId: v.id } } : {});
          },
          error: () => this.router.navigate([info.route!]),
        });
        break;
      case 'INV':
        this.salesInvoiceService.list(1, 10, { search: ref }).subscribe({
          next: (res) => {
            const id = res.data?.[0]?.id;
            this.router.navigate([info.route!], id ? { queryParams: { viewId: id } } : {});
          },
          error: () => this.router.navigate([info.route!]),
        });
        break;
      case 'SPAY':
        this.salesPaymentService.list(1, 10, { search: ref }).subscribe({
          next: (res) => {
            const id = res.data?.[0]?.id;
            this.router.navigate([info.route!], id ? { queryParams: { viewId: id } } : {});
          },
          error: () => this.router.navigate([info.route!]),
        });
        break;
      case 'SR':
        this.salesReturnService.list(1, 10, { search: ref }).subscribe({
          next: (res) => {
            const id = res.data?.[0]?.id;
            this.router.navigate([info.route!], id ? { queryParams: { viewId: id } } : {});
          },
          error: () => this.router.navigate([info.route!]),
        });
        break;
      case 'JV':
        this.journalEntryService.list(1, 10, { search: ref }).subscribe({
          next: (res) => {
            const id = res.data?.[0]?.id;
            this.router.navigate([info.route!], id ? { queryParams: { viewId: id } } : {});
          },
          error: () => this.router.navigate([info.route!]),
        });
        break;
      default:
        this.router.navigate([info.route!]);
    }
  }

  showJeDetail(journalEntryId: number): void {
    if (this.jeCache.has(journalEntryId)) {
      this.jeDetail.set(this.jeCache.get(journalEntryId));
      this.jeDialogVisible.set(true);
      return;
    }
    this.jeLoading.set(true);
    this.jeDialogVisible.set(true);
    this.jeDetail.set(null);
    this.journalEntryService.getById(journalEntryId).subscribe({
      next: (res) => {
        this.jeCache.set(journalEntryId, res.data);
        this.jeDetail.set(res.data);
        this.jeLoading.set(false);
      },
      error: () => this.jeLoading.set(false),
    });
  }

  getTypeBadge(type: string): { label: string; severity: 'info' | 'warn' | 'success' | 'danger' | 'secondary' | 'contrast' } {
    return type === 'petty_cash'
      ? { label: this.translate.instant('CASH_MOVEMENT.TYPE_PETTY_CASH'), severity: 'warn' }
      : { label: this.translate.instant('CASH_MOVEMENT.TYPE_BANK'), severity: 'info' };
  }

  getRunningBalance(ledger: AccountLedgerData, index: number): number {
    let balance = ledger.openingBalance;
    for (let i = 0; i <= index; i++) {
      balance += (+ledger.entries[i].debit || 0) - (+ledger.entries[i].credit || 0);
    }
    return balance;
  }

  exportExcel(): void {
    const allRows = this.buildExportRows();
    const columns: ExportColumn[] = [
      { field: 'accountName', header: 'CASH_MOVEMENT.ACCOUNT' },
      { field: 'date', header: 'CASH_MOVEMENT.DATE' },
      { field: 'source', header: 'CASH_MOVEMENT.SOURCE' },
      { field: 'entryNumber', header: 'CASH_MOVEMENT.DOCUMENT' },
      { field: 'description', header: 'CASH_MOVEMENT.DESCRIPTION' },
      { field: 'debit', header: 'CASH_MOVEMENT.DEBIT' },
      { field: 'credit', header: 'CASH_MOVEMENT.CREDIT' },
      { field: 'balance', header: 'CASH_MOVEMENT.BALANCE' },
    ];
    this.exportService.exportExcel(allRows, columns, 'cash-movement');
  }

  exportPdf(): void {
    const allRows = this.buildExportRows();
    const columns: ExportColumn[] = [
      { field: 'accountName', header: 'CASH_MOVEMENT.ACCOUNT' },
      { field: 'date', header: 'CASH_MOVEMENT.DATE' },
      { field: 'source', header: 'CASH_MOVEMENT.SOURCE' },
      { field: 'entryNumber', header: 'CASH_MOVEMENT.DOCUMENT' },
      { field: 'description', header: 'CASH_MOVEMENT.DESCRIPTION' },
      { field: 'debit', header: 'CASH_MOVEMENT.DEBIT' },
      { field: 'credit', header: 'CASH_MOVEMENT.CREDIT' },
      { field: 'balance', header: 'CASH_MOVEMENT.BALANCE' },
    ];
    this.exportService.exportPdf(allRows, columns, 'cash-movement', 'CASH_MOVEMENT.TITLE');
  }

  printReport(): void {
    window.print();
  }

  private buildExportRows(): any[] {
    const rows: any[] = [];
    for (const ledger of this.accountLedgers()) {
      ledger.entries.forEach((entry, idx) => {
        const src = this.getSourceInfo(entry);
        rows.push({
          accountName: ledger.account.name,
          date: entry.date,
          source: src.label,
          entryNumber: this.getDocNumber(entry),
          description: entry.description || entry.entry_description || '',
          debit: +entry.debit || 0,
          credit: +entry.credit || 0,
          balance: this.getRunningBalance(ledger, idx),
        });
      });
    }
    return rows;
  }

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