import type { BillCandidate, BillType, EnrichedLinkedBill, LinkedBill, PaymentRecord, PaymentRecordFilters, PaymentKpiDetailFilters } from '../types';
import { formatMoney as formatMoneyCore } from '../../../common/format-number.js';

export function formatMoney(value: number): string {
  if (value == null || Number.isNaN(value)) return '—';
  return formatMoneyCore(value);
}

export function calcBillUnreceivedAmount(receivable: number | null | undefined, allocatedAmount: number): number | null {
  if (receivable == null || Number.isNaN(receivable)) return null;
  const allocated = allocatedAmount || 0;
  return Math.round((receivable - allocated) * 100) / 100;
}

export function calcLinkedTotal(record: PaymentRecord): number {
  return (record.linkedBills || []).reduce((sum, bill) => sum + (bill.allocatedAmount || 0), 0);
}

export function calcUnclaimedAmount(record: PaymentRecord): number {
  const linked = calcLinkedTotal(record);
  return Math.max(0, Math.round((record.amount - linked) * 100) / 100);
}

export function calcRecordBillUnreceivedAmount(record: PaymentRecord, billPool: BillCandidate[]): number {
  const enriched = enrichLinkedBills(record.linkedBills || [], billPool, record.customerName);
  return enriched.reduce((sum, bill) => {
    const unreceived = calcBillUnreceivedAmount(bill.receivable, bill.allocatedAmount);
    return sum + (unreceived ?? 0);
  }, 0);
}

export function applyPaymentFilters(
  records: PaymentRecord[],
  filters: PaymentRecordFilters,
  billPool: BillCandidate[],
): PaymentRecord[] {
  return records.filter((row) => {
    if (filters.customerName.trim()) {
      if (row.customerName !== filters.customerName.trim()) return false;
    }
    if (filters.companyEntity.trim()) {
      if (row.companyEntity !== filters.companyEntity.trim()) return false;
    }
    if (filters.bank.trim()) {
      if (row.bank !== filters.bank.trim()) return false;
    }
    if (filters.operatorName.trim()) {
      if (row.operatorName !== filters.operatorName.trim()) return false;
    }
    if (filters.paymentFrom && row.paymentTime.slice(0, 10) < filters.paymentFrom) return false;
    if (filters.paymentTo && row.paymentTime.slice(0, 10) > filters.paymentTo) return false;
    const unlinked = calcUnclaimedAmount(row);
    if (filters.unclaimed === 'yes' && unlinked <= 0) return false;
    if (filters.unclaimed === 'no' && unlinked > 0) return false;
    return true;
  });
}

export function buildOperatorOptions(records: PaymentRecord[]): string[] {
  const set = new Set<string>();
  records.forEach((row) => {
    if (row.operatorName) set.add(row.operatorName);
  });
  return Array.from(set).sort();
}

export function buildCustomerOptions(records: PaymentRecord[]): Array<{ value: string; label: string }> {
  const set = new Set<string>();
  records.forEach((row) => {
    if (row.customerName) set.add(row.customerName);
  });
  return Array.from(set).sort().map((name) => ({ value: name, label: name }));
}

function buildFieldOptions(records: PaymentRecord[], field: 'companyEntity' | 'bank'): Array<{ value: string; label: string }> {
  const set = new Set<string>();
  records.forEach((row) => {
    const value = row[field];
    if (value) set.add(value);
  });
  return Array.from(set).sort().map((name) => ({ value: name, label: name }));
}

export function buildCompanyEntityOptions(records: PaymentRecord[]): Array<{ value: string; label: string }> {
  return buildFieldOptions(records, 'companyEntity');
}

export function buildBankOptions(records: PaymentRecord[]): Array<{ value: string; label: string }> {
  return buildFieldOptions(records, 'bank');
}

export function sumPaymentAmount(records: PaymentRecord[]): number {
  return records.reduce((sum, row) => sum + (row.amount || 0), 0);
}

export function sumLinkedAmount(records: PaymentRecord[]): number {
  return records.reduce((sum, row) => sum + calcLinkedTotal(row), 0);
}

export function sumUnclaimedAmount(records: PaymentRecord[]): number {
  return records.reduce((sum, row) => sum + calcUnclaimedAmount(row), 0);
}

export function sumBillUnreceivedAmount(records: PaymentRecord[], billPool: BillCandidate[]): number {
  return records.reduce((sum, record) => sum + calcRecordBillUnreceivedAmount(record, billPool), 0);
}

export type PaymentKpiKey = 'received' | 'linked' | 'unlinked';

export interface PaymentReceivedDetailRow {
  key: string;
  paymentTime: string;
  customerName: string;
  amount: number;
  remark: string;
}

export interface PaymentLinkedDetailRow {
  key: string;
  paymentTime: string;
  customerName: string;
  amount: number;
  linkedAmount: number;
  remark: string;
}

export interface PaymentUnlinkedDetailRow {
  key: string;
  paymentTime: string;
  customerName: string;
  amount: number;
  linkedAmount: number;
  unlinkedAmount: number;
  remark: string;
}

export function buildReceivedDetailRows(records: PaymentRecord[]): PaymentReceivedDetailRow[] {
  return records.map((record) => ({
    key: record.id,
    paymentTime: record.paymentTime,
    customerName: record.customerName,
    amount: record.amount,
    remark: record.remark || '—',
  }));
}

export function buildLinkedDetailRows(records: PaymentRecord[]): PaymentLinkedDetailRow[] {
  return records
    .map((record) => ({
      key: record.id,
      paymentTime: record.paymentTime,
      customerName: record.customerName,
      amount: record.amount,
      linkedAmount: calcLinkedTotal(record),
      remark: record.remark || '—',
    }))
    .filter((row) => row.linkedAmount > 0);
}

export function buildUnlinkedDetailRows(records: PaymentRecord[]): PaymentUnlinkedDetailRow[] {
  return records
    .map((record) => {
      const linkedAmount = calcLinkedTotal(record);
      return {
        key: record.id,
        paymentTime: record.paymentTime,
        customerName: record.customerName,
        amount: record.amount,
        linkedAmount,
        unlinkedAmount: calcUnclaimedAmount(record),
        remark: record.remark || '—',
      };
    })
    .filter((row) => row.unlinkedAmount > 0);
}

const BILL_TYPE_SHORT: Record<BillType, string> = {
  lease: '租赁',
  'self-operated': '自营',
  clearing: '氢费',
};

export function enrichLinkedBills(
  linkedBills: LinkedBill[],
  billPool: BillCandidate[],
  paymentCustomerName?: string,
): EnrichedLinkedBill[] {
  return (linkedBills || []).map((bill) => {
    const candidate = billPool.find((item) => item.id === bill.id && item.type === bill.type);
    return {
      ...bill,
      period: bill.period || candidate?.period,
      receivable: candidate?.receivable,
      customerName: bill.customerName || candidate?.customerName || paymentCustomerName || '—',
      projectName: bill.projectName || candidate?.projectName || '—',
      contractCode: bill.contractCode || candidate?.contractCode || '—',
    };
  });
}

export function flattenBillPool(pool: Record<string, BillCandidate[]>): BillCandidate[] {
  return Object.values(pool).flat();
}

export function filterBillsByTypeAndCustomer(
  bills: BillCandidate[],
  type: BillType | 'all',
  customerName: string,
): BillCandidate[] {
  return bills.filter((bill) => {
    if (type !== 'all' && bill.type !== type) return false;
    if (customerName && bill.customerName !== customerName) return false;
    return bill.unreceived > 0;
  });
}

function matchKpiDetailTimeAndCustomer(
  paymentTime: string,
  customerName: string,
  filters: PaymentKpiDetailFilters,
): boolean {
  if (filters.customerName && customerName !== filters.customerName) return false;
  if (filters.paymentFrom && paymentTime.slice(0, 10) < filters.paymentFrom) return false;
  if (filters.paymentTo && paymentTime.slice(0, 10) > filters.paymentTo) return false;
  return true;
}

export function filterReceivedDetailRows(
  rows: PaymentReceivedDetailRow[],
  filters: PaymentKpiDetailFilters,
): PaymentReceivedDetailRow[] {
  return rows.filter((row) => matchKpiDetailTimeAndCustomer(row.paymentTime, row.customerName, filters));
}

export function filterLinkedDetailRows(
  rows: PaymentLinkedDetailRow[],
  filters: PaymentKpiDetailFilters,
): PaymentLinkedDetailRow[] {
  return rows.filter((row) => matchKpiDetailTimeAndCustomer(row.paymentTime, row.customerName, filters));
}

export function filterUnlinkedDetailRows(
  rows: PaymentUnlinkedDetailRow[],
  filters: PaymentKpiDetailFilters,
): PaymentUnlinkedDetailRow[] {
  return rows.filter((row) => matchKpiDetailTimeAndCustomer(row.paymentTime, row.customerName, filters));
}

export function sumReceivedDetailAmount(rows: PaymentReceivedDetailRow[]): number {
  return rows.reduce((sum, row) => sum + (row.amount || 0), 0);
}

export function sumLinkedDetailAmount(rows: PaymentLinkedDetailRow[]): number {
  return rows.reduce((sum, row) => sum + (row.linkedAmount || 0), 0);
}

export function sumUnlinkedDetailAmount(rows: PaymentUnlinkedDetailRow[]): number {
  return rows.reduce((sum, row) => sum + (row.unlinkedAmount || 0), 0);
}

export function buildDetailCustomerOptions(names: string[]): Array<{ value: string; label: string }> {
  const set = new Set<string>();
  names.forEach((name) => {
    if (name) set.add(name);
  });
  return Array.from(set).sort().map((name) => ({ value: name, label: name }));
}

function escapeCsvCell(value: string | number): string {
  const str = String(value ?? '');
  if (/[",\n\r]/.test(str)) return `"${str.replace(/"/g, '""')}"`;
  return str;
}

function downloadCsv(filename: string, headers: string[], rows: Array<Array<string | number>>) {
  const bom = '\uFEFF';
  const lines = [
    headers.map(escapeCsvCell).join(','),
    ...rows.map((row) => row.map(escapeCsvCell).join(',')),
  ];
  const blob = new Blob([bom + lines.join('\n')], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement('a');
  anchor.href = url;
  anchor.download = filename;
  anchor.click();
  URL.revokeObjectURL(url);
}

const KPI_EXPORT_FILENAME: Record<PaymentKpiKey, string> = {
  received: '到账总金额明细',
  linked: '认领总金额明细',
  unlinked: '未关联总金额明细',
};

export function exportKpiDetailCsv(
  kpiKey: PaymentKpiKey,
  receivedRows: PaymentReceivedDetailRow[],
  linkedRows: PaymentLinkedDetailRow[],
  unlinkedRows: PaymentUnlinkedDetailRow[],
): number {
  const stamp = new Date().getTime();
  if (kpiKey === 'received') {
    downloadCsv(
      `${KPI_EXPORT_FILENAME.received}_${stamp}.csv`,
      ['收款时间', '客户名称', '到账金额', '备注'],
      receivedRows.map((row) => [row.paymentTime, row.customerName, row.amount, row.remark]),
    );
    return receivedRows.length;
  }
  if (kpiKey === 'linked') {
    downloadCsv(
      `${KPI_EXPORT_FILENAME.linked}_${stamp}.csv`,
      ['收款时间', '客户名称', '到账金额', '认领金额', '备注'],
      linkedRows.map((row) => [
        row.paymentTime,
        row.customerName,
        row.amount,
        row.linkedAmount,
        row.remark,
      ]),
    );
    return linkedRows.length;
  }
  downloadCsv(
    `${KPI_EXPORT_FILENAME.unlinked}_${stamp}.csv`,
    ['收款时间', '客户名称', '到账金额', '已认领', '未关联金额', '备注'],
    unlinkedRows.map((row) => [
      row.paymentTime,
      row.customerName,
      row.amount,
      row.linkedAmount,
      row.unlinkedAmount,
      row.remark,
    ]),
  );
  return unlinkedRows.length;
}
