import { Component, OnInit, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { GridModule, PageChangeEvent, CellClickEvent } from '@progress/kendo-angular-grid';
import { ButtonsModule } from '@progress/kendo-angular-buttons';
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { DialogsModule } from '@progress/kendo-angular-dialog';
import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
import { ContextMenuModule, ContextMenuComponent, ContextMenuSelectEvent } from '@progress/kendo-angular-menu';
import { EXPENSE_STATUS_OPTIONS } from '../../../../shared/i18n/option-lists';
import { ExpenseApiService, ExpenseQuery } from '../../services/expense-api.service';
import { MinistryApiService } from '../../services/ministry-api.service';
import { ExpenseFormDialogComponent, ExpenseFormResult } from '../../components/expense-form-dialog/expense-form-dialog.component';
import { ExpenseReviewDialogComponent } from '../../components/expense-review-dialog/expense-review-dialog.component';
import { ExpenseDto, ExpenseListItemDto, MinistryDto } from '../../models/expense.model';
import { switchMap, of } from 'rxjs';

@Component({
  selector: 'app-expenses-page',
  standalone: true,
  imports: [
    CommonModule, FormsModule, GridModule, ButtonsModule, DropDownsModule,
    InputsModule, DialogsModule, DateInputsModule, ContextMenuModule,
    ExpenseFormDialogComponent, ExpenseReviewDialogComponent,
  ],
  templateUrl: './expenses-page.component.html',
  styleUrls: ['./expenses-page.component.scss'],
})
export class ExpensesPageComponent implements OnInit {
  rows: ExpenseListItemDto[] = [];
  total = 0;
  page = 1;
  pageSize = 20;
  loading = false;

  ministries: MinistryDto[] = [];
  readonly statuses = EXPENSE_STATUS_OPTIONS;

  filter: ExpenseQuery = {};

  vendorDialogOpen = false;
  reimbDialogOpen = false;

  editRow: ExpenseDto | null = null;
  editMode: 'vendor' | 'reimbursement' = 'reimbursement';

  payRow: ExpenseListItemDto | null = null;
  payCheckNumber = '';
  payDate = new Date();

  /** Row whose detail+receipt are open in the review dialog for an approve/reject decision. */
  reviewRow: ExpenseListItemDto | null = null;

  /** Right-click row-action menu: items are rebuilt per row from what that row currently allows. */
  @ViewChild('rowMenu') rowMenu!: ContextMenuComponent;
  rowMenuItems: { text: string }[] = [];
  private contextRow: ExpenseListItemDto | null = null;

  /** Transient confirmation pill, used so the user gets feedback during continuous entry. */
  toast: string | null = null;
  private toastTimer?: ReturnType<typeof setTimeout>;

  constructor(private api: ExpenseApiService, private ministryApi: MinistryApiService) { }

  ngOnInit(): void {
    this.ministryApi.getAll().subscribe(m => (this.ministries = m));
    this.load();
  }

  load(): void {
    this.loading = true;
    this.api.getPaged({ ...this.filter, page: this.page, pageSize: this.pageSize }).subscribe({
      next: r => { this.rows = r.items; this.total = r.totalCount; this.loading = false; },
      error: () => { this.loading = false; },
    });
  }

  get skip(): number { return (this.page - 1) * this.pageSize; }

  applyFilter(): void { this.page = 1; this.load(); }

  onPageChange(e: PageChangeEvent): void {
    this.page = Math.floor(e.skip / this.pageSize) + 1;
    this.load();
  }

  // ── Mobile pager (the Kendo grid pager is desktop-only) ───────────────────────
  get totalPages(): number { return Math.max(1, Math.ceil(this.total / this.pageSize)); }

  prevPage(): void {
    if (this.page <= 1) return;
    this.page--;
    this.load();
  }

  nextPage(): void {
    if (this.page >= this.totalPages) return;
    this.page++;
    this.load();
  }

  // ── Row interaction: right-click opens the per-row action menu ────────────────
  onCellClick(event: CellClickEvent): void {
    if (event.type !== 'contextmenu') return;
    event.originalEvent.preventDefault();
    const items = this.buildMenuItems(event.dataItem);
    if (items.length === 0) return;
    this.contextRow = event.dataItem;
    this.rowMenuItems = items;
    this.rowMenu.show({ left: event.originalEvent.pageX, top: event.originalEvent.pageY });
  }

  onRowMenuSelect(event: ContextMenuSelectEvent): void {
    const row = this.contextRow;
    if (!row) return;
    switch (event.item.text) {
      case 'Edit': this.openEdit(row); break;
      case 'Review': this.openReview(row); break;
      case 'Pay': this.openPay(row); break;
      case 'Receipt': this.openReceipt(row.id); break;
    }
  }

  private buildMenuItems(row: ExpenseListItemDto): { text: string }[] {
    const items: { text: string }[] = [];
    if (this.canEdit(row)) items.push({ text: 'Edit' });
    if (this.canApproveOrReject(row)) items.push({ text: 'Review' });
    if (this.canPay(row)) items.push({ text: 'Pay' });
    if (row.hasReceipt) items.push({ text: 'Receipt' });
    return items;
  }

  onVendorSave(result: ExpenseFormResult): void {
    this.api.create(result.request).subscribe(() => { this.vendorDialogOpen = false; this.load(); });
  }

  onReimbSave(result: ExpenseFormResult): void {
    this.api.create(result.request).pipe(
      switchMap(c => result.receipt
        ? this.api.uploadReceipt(c.id, result.receipt).pipe(switchMap(() => of(c)))
        : of(c)),
    ).subscribe(() => {
      // In continuous-entry mode the dialog resets itself and stays open for the next entry.
      if (!result.continueEntry) this.reimbDialogOpen = false;
      this.showToast('已儲存 ✓ Saved');
      this.load();
    });
  }

  openEdit(row: ExpenseListItemDto): void {
    // Fetch the full expense (with its lines) before opening the dialog for editing.
    this.editMode = row.type === 'VendorPayment' ? 'vendor' : 'reimbursement';
    this.api.getById(row.id).subscribe(dto => (this.editRow = dto));
  }

  closeEdit(): void { this.editRow = null; }

  onEditSave(result: ExpenseFormResult): void {
    if (!this.editRow) return;
    const id = this.editRow.id;
    this.api.update(id, result.request).pipe(
      switchMap(() => result.receipt ? this.api.uploadReceipt(id, result.receipt) : of(void 0)),
    ).subscribe(() => { this.closeEdit(); this.load(); });
  }

  openReview(row: ExpenseListItemDto): void { this.reviewRow = row; }
  closeReview(): void { this.reviewRow = null; }

  onReviewApprove(): void {
    if (!this.reviewRow) return;
    this.api.approve(this.reviewRow.id).subscribe(() => { this.reviewRow = null; this.load(); });
  }

  onReviewReject(reviewNotes: string): void {
    if (!this.reviewRow) return;
    this.api.reject(this.reviewRow.id, { reviewNotes: reviewNotes || null }).subscribe(() => {
      this.reviewRow = null;
      this.load();
    });
  }

  openPay(row: ExpenseListItemDto): void {
    this.payRow = row;
    this.payCheckNumber = '';
    this.payDate = new Date();
  }

  confirmPay(): void {
    if (!this.payRow) return;
    const d = this.payDate;
    const paidAt = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
    this.api.pay(this.payRow.id, { checkNumber: this.payCheckNumber || null, paidAt }).subscribe(() => {
      this.payRow = null;
      this.load();
    });
  }

  openReceipt(id: number): void {
    this.api.downloadReceipt(id).subscribe(blob => {
      const url = URL.createObjectURL(blob);
      window.open(url, '_blank');
      setTimeout(() => URL.revokeObjectURL(url), 60_000);
    });
  }

  /** Finance may edit (and reupload the receipt) while the expense is still Draft or awaiting review. */
  canEdit(row: ExpenseListItemDto): boolean { return row.status === 'Draft' || row.status === 'PendingApproval'; }
  canApproveOrReject(row: ExpenseListItemDto): boolean { return row.status === 'PendingApproval'; }
  canPay(row: ExpenseListItemDto): boolean {
    return false;
    // row.status === 'Approved'; 
    //should be pay by disbursement
  }

  private showToast(message: string): void {
    this.toast = message;
    if (this.toastTimer) clearTimeout(this.toastTimer);
    this.toastTimer = setTimeout(() => (this.toast = null), 2200);
  }

  statusClass(status: string): string {
    return ({
      Draft: 'badge-draft',
      PendingApproval: 'badge-pending',
      Approved: 'badge-approved',
      Paid: 'badge-paid',
      Rejected: 'badge-rejected',
    } as Record<string, string>)[status] ?? '';
  }
}
