import type {
  Customer,
  CustomerPayment,
  InventoryMovement,
  JournalEntry,
  PurchaseOrder,
  SalesInvoice,
  SalesRep,
  Supplier,
} from '@/lib/types';

type MaterialLike = {
  id: string;
  name: string;
  purchasePrice?: number;
  reorderMinimumQty?: number;
};

export type ReportFilters = {
  fromDate?: string;
  toDate?: string;
  customerId?: string;
  supplierId?: string;
  salesRepId?: string;
  materialId?: string;
};

export type SalesSummary = {
  invoiceCount: number;
  totalSales: number;
  totalPaid: number;
  totalDue: number;
};

export type SalesByItemRow = {
  materialId: string;
  materialName: string;
  quantitySold: number;
  salesValue: number;
  estimatedCost: number;
  estimatedProfit: number;
};

export type TopSellingRow = SalesByItemRow;

export type StaleItemRow = {
  materialId: string;
  materialName: string;
  lastMovementDate: string;
  daysSinceLastMove: number;
  currentStockQty: number;
};

export type CurrentStockRow = {
  materialId: string;
  materialName: string;
  quantityIn: number;
  quantityOut: number;
  balanceQty: number;
};

export type ItemMovementRow = {
  movementId: string;
  date: string;
  referenceType: string;
  referenceId: string;
  quantityIn: number;
  quantityOut: number;
  delta: number;
  runningQty: number;
};

export type InventoryCostRow = {
  materialId: string;
  materialName: string;
  stockQty: number;
  unitCost: number;
  stockValue: number;
};

export type PurchaseSummary = {
  invoiceCount: number;
  totalPurchases: number;
  totalPaid: number;
  totalDue: number;
};

export type PurchaseBySupplierRow = {
  supplierId: string;
  supplierName: string;
  lineCount: number;
  totalQuantity: number;
  totalValue: number;
};

export type AgingRow = {
  partyId: string;
  partyName: string;
  partyType: 'customer' | 'supplier';
  bucket0to30: number;
  bucket31to60: number;
  bucket61to90: number;
  bucket90plus: number;
  total: number;
};

export type CollectionRow = {
  customerId: string;
  customerName: string;
  totalBilled: number;
  totalPaid: number;
  remaining: number;
  lastPaymentDate: string;
  lastPaymentAmount: number;
  daysSinceLastPayment: number | null;
};

export type OpenInvoiceRow = {
  invoiceId: string;
  invoiceNumber: string;
  date: string;
  customerId: string;
  customerName: string;
  grandTotal: number;
  amountPaid: number;
  amountDue: number;
};

export type CustomerProfitabilityRow = {
  customerId: string;
  customerName: string;
  salesValue: number;
  estimatedCost: number;
  estimatedProfit: number;
};

export type SalesRepPerformanceRow = {
  salesRepId: string;
  salesRepName: string;
  invoiceCount: number;
  totalSales: number;
  averageInvoice: number;
};

export type InventoryTurnover = {
  cogsEstimate: number;
  openingStockValue: number;
  closingStockValue: number;
  averageStockValue: number;
  turnoverRate: number;
};

export type ReorderRow = {
  materialId: string;
  materialName: string;
  currentQty: number;
  reorderMinimumQty: number;
  shortageQty: number;
};

export type ReturnsRow = {
  invoiceId: string;
  invoiceNumber: string;
  date: string;
  customerId: string;
  customerName: string;
  returnValue: number;
};

export type PosShiftSessionLike = {
  id?: string;
  userId?: string;
  userName?: string;
  startedAt?: string;
  endedAt?: string;
  status?: 'active' | 'closed';
  openingCash?: number;
  openingVisa?: number;
  totalSalesAmount?: number;
  invoicesCount?: number;
  returnsCount?: number;
  cashSalesAmount?: number;
  visaSalesAmount?: number;
  receivablesAmount?: number;
  couponAmount?: number;
  expectedCashAtClose?: number;
  expectedVisaAtClose?: number;
  countedCashAtClose?: number;
  countedVisaAtClose?: number;
  cashVariance?: number;
  visaVariance?: number;
  requiresApproval?: boolean;
  cashVarianceHandled?: boolean;
};

export type PosShiftRow = {
  id: string;
  userId: string;
  userName: string;
  startedAt: string;
  endedAt: string;
  status: 'active' | 'closed';
  openingCash: number;
  openingVisa: number;
  totalSalesAmount: number;
  invoicesCount: number;
  returnsCount: number;
  cashSalesAmount: number;
  visaSalesAmount: number;
  receivablesAmount: number;
  couponAmount: number;
  expectedCashAtClose: number;
  expectedVisaAtClose: number;
  countedCashAtClose: number;
  countedVisaAtClose: number;
  cashVariance: number;
  visaVariance: number;
  requiresApproval: boolean;
  cashVarianceHandled: boolean;
};

export type PosShiftUserSummaryRow = {
  userId: string;
  userName: string;
  shiftsCount: number;
  activeShiftsCount: number;
  closedShiftsCount: number;
  invoicesCount: number;
  returnsCount: number;
  totalSalesAmount: number;
  cashSalesAmount: number;
  visaSalesAmount: number;
  receivablesAmount: number;
  couponAmount: number;
  openingCash: number;
  openingVisa: number;
  cashVariance: number;
  visaVariance: number;
};

export type PosShiftReport = {
  sessions: PosShiftRow[];
  summaryByUser: PosShiftUserSummaryRow[];
  totals: {
    shiftsCount: number;
    activeShiftsCount: number;
    closedShiftsCount: number;
    invoicesCount: number;
    returnsCount: number;
    totalSalesAmount: number;
    cashVariance: number;
    visaVariance: number;
  };
};

function normDate(value?: string): string {
  if (!value) return '';
  return String(value).trim().slice(0, 10);
}

function toNumber(value: unknown): number {
  const numberValue = Number(value || 0);
  return Number.isFinite(numberValue) ? numberValue : 0;
}

function withinRange(value: string, fromDate?: string, toDate?: string): boolean {
  const d = normDate(value);
  const from = normDate(fromDate);
  const to = normDate(toDate);
  if (from && d && d < from) return false;
  if (to && d && d > to) return false;
  return true;
}

function withinDateTimeRange(value: string, fromDate?: string, toDate?: string): boolean {
  const d = normDate(value);
  return withinRange(d, fromDate, toDate);
}

function daysBetween(today: Date, value: string): number {
  if (!value) return Number.MAX_SAFE_INTEGER;
  const inputDate = new Date(`${normDate(value)}T00:00:00.000Z`);
  if (Number.isNaN(inputDate.getTime())) return Number.MAX_SAFE_INTEGER;
  const diff = today.getTime() - inputDate.getTime();
  return Math.max(0, Math.floor(diff / (24 * 60 * 60 * 1000)));
}

function activeSalesInvoices(invoices: SalesInvoice[]) {
  return (invoices || []).filter((invoice) => String(invoice.status || 'active') === 'active');
}

function filteredSalesInvoices(invoices: SalesInvoice[], filters: ReportFilters) {
  return activeSalesInvoices(invoices).filter((invoice) => {
    if (!withinRange(invoice.date, filters.fromDate, filters.toDate)) return false;
    if (filters.customerId && String(invoice.customerId || '').trim() !== String(filters.customerId || '').trim()) return false;
    if (filters.salesRepId && String(invoice.salesRepId || '').trim() !== String(filters.salesRepId || '').trim()) return false;
    return true;
  });
}

function filteredPurchaseOrders(orders: PurchaseOrder[], filters: ReportFilters) {
  return (orders || []).filter((order) => {
    if (String(order.status || 'active') !== 'active' && String(order.status || 'active') !== 'received') return false;
    if (!withinRange(order.date, filters.fromDate, filters.toDate)) return false;
    if (filters.supplierId && String(order.supplierId || '').trim() !== String(filters.supplierId || '').trim()) return false;
    return true;
  });
}

function filteredMovements(movements: InventoryMovement[], filters: ReportFilters) {
  return (movements || []).filter((movement) => {
    if (!withinRange(movement.date, filters.fromDate, filters.toDate)) return false;
    if (filters.materialId && String(movement.materialId || '').trim() !== String(filters.materialId || '').trim()) return false;
    return true;
  });
}

function materialIndex(materials: MaterialLike[]) {
  const byId = new Map<string, MaterialLike>();
  (materials || []).forEach((material) => {
    byId.set(String(material.id || '').trim(), material);
  });
  return byId;
}

export function buildSalesSummary(invoices: SalesInvoice[], filters: ReportFilters): SalesSummary {
  const rows = filteredSalesInvoices(invoices, filters).filter((invoice) => String(invoice.type || 'sale') !== 'return');
  return {
    invoiceCount: rows.length,
    totalSales: rows.reduce((sum, row) => sum + toNumber(row.grandTotal), 0),
    totalPaid: rows.reduce((sum, row) => sum + toNumber(row.amountPaid), 0),
    totalDue: rows.reduce((sum, row) => sum + toNumber(row.amountDue), 0),
  };
}

export function buildSalesByItem(invoices: SalesInvoice[], materials: MaterialLike[], filters: ReportFilters): SalesByItemRow[] {
  const rows = filteredSalesInvoices(invoices, filters).filter((invoice) => String(invoice.type || 'sale') !== 'return');
  const materialsById = materialIndex(materials);
  const map = new Map<string, SalesByItemRow>();

  rows.forEach((invoice) => {
    (invoice.items || []).forEach((item) => {
      const materialId = String(item.materialId || '').trim();
      const material = materialsById.get(materialId);
      const quantity = toNumber(item.quantity);
      const salesValue = toNumber(item.total || quantity * toNumber(item.unitPrice));
      const unitCost = toNumber(material?.purchasePrice);
      const estimatedCost = quantity * unitCost;
      const key = materialId || String(item.name || '').trim() || 'unknown';
      const current = map.get(key) || {
        materialId: materialId || key,
        materialName: String(material?.name || item.name || 'غير معروف').trim(),
        quantitySold: 0,
        salesValue: 0,
        estimatedCost: 0,
        estimatedProfit: 0,
      };
      current.quantitySold += quantity;
      current.salesValue += salesValue;
      current.estimatedCost += estimatedCost;
      current.estimatedProfit = current.salesValue - current.estimatedCost;
      map.set(key, current);
    });
  });

  return Array.from(map.values()).sort((left, right) => right.salesValue - left.salesValue);
}

export function buildTopSellingItems(rows: SalesByItemRow[], sortBy: 'quantity' | 'value', limit = 10): TopSellingRow[] {
  const sorted = [...rows].sort((left, right) => {
    return sortBy === 'quantity' ? right.quantitySold - left.quantitySold : right.salesValue - left.salesValue;
  });
  return sorted.slice(0, Math.max(1, limit));
}

function computeCurrentStockMap(movements: InventoryMovement[]) {
  const map = new Map<string, CurrentStockRow>();
  (movements || []).forEach((movement) => {
    const materialId = String(movement.materialId || '').trim();
    if (!materialId) return;
    const current = map.get(materialId) || {
      materialId,
      materialName: materialId,
      quantityIn: 0,
      quantityOut: 0,
      balanceQty: 0,
    };
    current.quantityIn += toNumber(movement.quantityIn);
    current.quantityOut += toNumber(movement.quantityOut);
    current.balanceQty = current.quantityIn - current.quantityOut;
    map.set(materialId, current);
  });
  return map;
}

export function buildCurrentStock(movements: InventoryMovement[], materials: MaterialLike[], filters: ReportFilters): CurrentStockRow[] {
  const byId = materialIndex(materials);
  const stockMap = computeCurrentStockMap(filteredMovements(movements, filters));
  return Array.from(stockMap.values())
    .map((row) => ({
      ...row,
      materialName: String(byId.get(row.materialId)?.name || row.materialId),
    }))
    .sort((left, right) => left.materialName.localeCompare(right.materialName, 'ar'));
}

export function buildItemMovementReport(movements: InventoryMovement[], materialId: string, filters: ReportFilters): ItemMovementRow[] {
  if (!materialId) return [];
  const rows = filteredMovements(movements, { ...filters, materialId })
    .sort((left, right) => normDate(left.date).localeCompare(normDate(right.date)) || String(left.id || '').localeCompare(String(right.id || '')));

  let running = 0;
  return rows.map((movement) => {
    const qtyIn = toNumber(movement.quantityIn);
    const qtyOut = toNumber(movement.quantityOut);
    const delta = qtyIn - qtyOut;
    running += delta;
    return {
      movementId: String(movement.id || '').trim(),
      date: normDate(movement.date),
      referenceType: String(movement.referenceType || '').trim(),
      referenceId: String(movement.referenceId || '').trim(),
      quantityIn: qtyIn,
      quantityOut: qtyOut,
      delta,
      runningQty: running,
    };
  });
}

export function buildInventoryCostReport(movements: InventoryMovement[], materials: MaterialLike[], filters: ReportFilters): InventoryCostRow[] {
  const stockRows = buildCurrentStock(movements, materials, filters);
  const materialsById = materialIndex(materials);
  return stockRows.map((row) => {
    const material = materialsById.get(row.materialId);
    const unitCost = toNumber(material?.purchasePrice);
    return {
      materialId: row.materialId,
      materialName: row.materialName,
      stockQty: row.balanceQty,
      unitCost,
      stockValue: row.balanceQty * unitCost,
    };
  }).sort((left, right) => right.stockValue - left.stockValue);
}

export function buildStaleItemsReport(movements: InventoryMovement[], materials: MaterialLike[], days = 60): StaleItemRow[] {
  const today = new Date();
  const stockMap = computeCurrentStockMap(movements);
  const lastDateByMaterial = new Map<string, string>();

  (movements || []).forEach((movement) => {
    const materialId = String(movement.materialId || '').trim();
    if (!materialId) return;
    const date = normDate(movement.date);
    const previous = normDate(lastDateByMaterial.get(materialId));
    if (!previous || date > previous) {
      lastDateByMaterial.set(materialId, date);
    }
  });

  return (materials || [])
    .map((material) => {
      const id = String(material.id || '').trim();
      const lastDate = lastDateByMaterial.get(id) || '';
      const daysSince = daysBetween(today, lastDate);
      const stock = stockMap.get(id)?.balanceQty || 0;
      return {
        materialId: id,
        materialName: String(material.name || '').trim() || id,
        lastMovementDate: lastDate,
        daysSinceLastMove: daysSince,
        currentStockQty: stock,
      };
    })
    .filter((row) => row.currentStockQty > 0)
    .filter((row) => row.daysSinceLastMove >= days)
    .sort((left, right) => right.daysSinceLastMove - left.daysSinceLastMove);
}

export function buildPurchaseSummary(orders: PurchaseOrder[], filters: ReportFilters): PurchaseSummary {
  const rows = filteredPurchaseOrders(orders, filters);
  return {
    invoiceCount: rows.length,
    totalPurchases: rows.reduce((sum, row) => sum + toNumber(row.grandTotal), 0),
    totalPaid: rows.reduce((sum, row) => sum + toNumber(row.amountPaid), 0),
    totalDue: rows.reduce((sum, row) => sum + toNumber(row.amountDue), 0),
  };
}

export function buildPurchaseBySupplier(orders: PurchaseOrder[], filters: ReportFilters): PurchaseBySupplierRow[] {
  const rows = filteredPurchaseOrders(orders, filters);
  const map = new Map<string, PurchaseBySupplierRow>();

  rows.forEach((order) => {
    const key = String(order.supplierId || '').trim() || String(order.supplierName || '').trim() || 'unknown';
    const current = map.get(key) || {
      supplierId: String(order.supplierId || '').trim() || key,
      supplierName: String(order.supplierName || '').trim() || 'غير معروف',
      lineCount: 0,
      totalQuantity: 0,
      totalValue: 0,
    };

    (order.items || []).forEach((item) => {
      current.lineCount += 1;
      current.totalQuantity += toNumber(item.quantity);
      current.totalValue += toNumber(item.total || toNumber(item.quantity) * toNumber(item.unitPrice));
    });

    map.set(key, current);
  });

  return Array.from(map.values()).sort((left, right) => right.totalValue - left.totalValue);
}

export function buildAgingReport(journalEntries: JournalEntry[], customers: Customer[], suppliers: Supplier[], asOfDate?: string): AgingRow[] {
  const endDate = normDate(asOfDate) || normDate(new Date().toISOString());
  const today = new Date(`${endDate}T00:00:00.000Z`);

  const customerByCode = new Map<string, Customer>();
  const customerByAccountId = new Map<string, Customer>();
  const supplierByCode = new Map<string, Supplier>();
  const supplierByAccountId = new Map<string, Supplier>();

  (customers || []).forEach((customer) => {
    if (customer.accountCode) customerByCode.set(String(customer.accountCode).trim(), customer);
    if (customer.accountId) customerByAccountId.set(String(customer.accountId).trim(), customer);
  });
  (suppliers || []).forEach((supplier) => {
    if (supplier.accountCode) supplierByCode.set(String(supplier.accountCode).trim(), supplier);
    if (supplier.accountId) supplierByAccountId.set(String(supplier.accountId).trim(), supplier);
  });

  const rows = new Map<string, AgingRow>();

  (journalEntries || []).forEach((entry) => {
    const entryDate = normDate(entry.date);
    if (entryDate && entryDate > endDate) return;

    const ageDays = daysBetween(today, entryDate);
    const bucketName = ageDays <= 30 ? 'bucket0to30' : ageDays <= 60 ? 'bucket31to60' : ageDays <= 90 ? 'bucket61to90' : 'bucket90plus';

    (entry.lines || []).forEach((line) => {
      const accountCode = String(line.accountCode || '').trim();
      const accountId = String(line.accountId || '').trim();
      const netDebit = toNumber(line.debit) - toNumber(line.credit);
      if (Math.abs(netDebit) < 1e-9) return;

      const customer = customerByCode.get(accountCode) || customerByAccountId.get(accountId);
      if (customer) {
        const key = `customer:${String(customer.id || '').trim()}`;
        const current = rows.get(key) || {
          partyId: String(customer.id || '').trim(),
          partyName: String(customer.name || '').trim() || '-',
          partyType: 'customer' as const,
          bucket0to30: 0,
          bucket31to60: 0,
          bucket61to90: 0,
          bucket90plus: 0,
          total: 0,
        };
        current[bucketName] += netDebit;
        current.total += netDebit;
        rows.set(key, current);
        return;
      }

      const supplier = supplierByCode.get(accountCode) || supplierByAccountId.get(accountId);
      if (supplier) {
        const key = `supplier:${String(supplier.id || '').trim()}`;
        const current = rows.get(key) || {
          partyId: String(supplier.id || '').trim(),
          partyName: String(supplier.name || '').trim() || '-',
          partyType: 'supplier' as const,
          bucket0to30: 0,
          bucket31to60: 0,
          bucket61to90: 0,
          bucket90plus: 0,
          total: 0,
        };
        const supplierNet = -netDebit;
        current[bucketName] += supplierNet;
        current.total += supplierNet;
        rows.set(key, current);
      }
    });
  });

  return Array.from(rows.values())
    .filter((row) => Math.abs(row.total) > 0.0001)
    .sort((left, right) => Math.abs(right.total) - Math.abs(left.total));
}

export function buildCollectionMonitoring(invoices: SalesInvoice[], payments: CustomerPayment[], filters: ReportFilters): CollectionRow[] {
  const salesInvoices = filteredSalesInvoices(invoices, filters).filter((invoice) => String(invoice.type || 'sale') !== 'return');
  const rows = new Map<string, CollectionRow>();
  const asOfDate = normDate(filters.toDate) || normDate(new Date().toISOString());
  const asOf = new Date(`${asOfDate}T00:00:00.000Z`);

  salesInvoices.forEach((invoice) => {
    const key = String(invoice.customerId || '').trim() || String(invoice.customerName || '').trim() || 'unknown';
    const current = rows.get(key) || {
      customerId: String(invoice.customerId || '').trim() || key,
      customerName: String(invoice.customerName || '').trim() || 'غير معروف',
      totalBilled: 0,
      totalPaid: 0,
      remaining: 0,
      lastPaymentDate: '',
      lastPaymentAmount: 0,
      daysSinceLastPayment: null,
    };
    current.totalBilled += toNumber(invoice.grandTotal);
    current.totalPaid += toNumber(invoice.amountPaid);
    rows.set(key, current);
  });

  const invoiceIdsInScope = new Set(salesInvoices.map((invoice) => String(invoice.id || '').trim()));

  (payments || []).forEach((payment) => {
    if (!withinRange(payment.date, filters.fromDate, filters.toDate)) return;
    if (String(payment.status || 'active') !== 'active') return;
    if (filters.customerId && String(payment.customerId || '').trim() !== String(filters.customerId || '').trim()) return;

    const allocated = (payment.allocations || []).filter((allocation) => invoiceIdsInScope.has(String(allocation.invoiceId || '').trim()));
    const allocatedAmount = allocated.reduce((sum, allocation) => sum + toNumber(allocation.amount), 0);
    if (allocatedAmount <= 0) return;

    const customerKey = String(payment.customerId || '').trim();
    const current = rows.get(customerKey) || {
      customerId: customerKey,
      customerName: customerKey,
      totalBilled: 0,
      totalPaid: 0,
      remaining: 0,
      lastPaymentDate: '',
      lastPaymentAmount: 0,
      daysSinceLastPayment: null,
    };

    const paymentDate = normDate(payment.date);
    if (paymentDate) {
      const previousDate = normDate(current.lastPaymentDate);
      if (!previousDate || paymentDate > previousDate) {
        current.lastPaymentDate = paymentDate;
        current.lastPaymentAmount = allocatedAmount;
      }
    }

    current.totalPaid += allocatedAmount;
    rows.set(customerKey, current);
  });

  return Array.from(rows.values())
    .map((row) => ({
      ...row,
      remaining: row.totalBilled - row.totalPaid,
      daysSinceLastPayment: row.lastPaymentDate ? daysBetween(asOf, row.lastPaymentDate) : null,
    }))
    .sort((left, right) => right.remaining - left.remaining);
}

export function buildOpenInvoices(invoices: SalesInvoice[], filters: ReportFilters): OpenInvoiceRow[] {
  return filteredSalesInvoices(invoices, filters)
    .filter((invoice) => String(invoice.type || 'sale') !== 'return')
    .filter((invoice) => toNumber(invoice.amountDue) > 0.0001)
    .map((invoice) => ({
      invoiceId: String(invoice.id || '').trim(),
      invoiceNumber: String(invoice.invoiceNumber || '').trim(),
      date: normDate(invoice.date),
      customerId: String(invoice.customerId || '').trim(),
      customerName: String(invoice.customerName || '').trim(),
      grandTotal: toNumber(invoice.grandTotal),
      amountPaid: toNumber(invoice.amountPaid),
      amountDue: toNumber(invoice.amountDue),
    }))
    .sort((left, right) => normDate(right.date).localeCompare(normDate(left.date)));
}

export function buildCustomerProfitability(invoices: SalesInvoice[], materials: MaterialLike[], filters: ReportFilters): CustomerProfitabilityRow[] {
  const rows = filteredSalesInvoices(invoices, filters).filter((invoice) => String(invoice.type || 'sale') !== 'return');
  const materialsById = materialIndex(materials);
  const map = new Map<string, CustomerProfitabilityRow>();

  rows.forEach((invoice) => {
    const key = String(invoice.customerId || '').trim() || String(invoice.customerName || '').trim() || 'unknown';
    const current = map.get(key) || {
      customerId: String(invoice.customerId || '').trim() || key,
      customerName: String(invoice.customerName || '').trim() || 'غير معروف',
      salesValue: 0,
      estimatedCost: 0,
      estimatedProfit: 0,
    };

    current.salesValue += toNumber(invoice.grandTotal);

    (invoice.items || []).forEach((item) => {
      const material = materialsById.get(String(item.materialId || '').trim());
      current.estimatedCost += toNumber(item.quantity) * toNumber(material?.purchasePrice);
    });

    current.estimatedProfit = current.salesValue - current.estimatedCost;
    map.set(key, current);
  });

  return Array.from(map.values()).sort((left, right) => right.estimatedProfit - left.estimatedProfit);
}

export function buildSalesRepPerformance(invoices: SalesInvoice[], reps: SalesRep[], filters: ReportFilters): SalesRepPerformanceRow[] {
  const rows = filteredSalesInvoices(invoices, filters).filter((invoice) => String(invoice.type || 'sale') !== 'return');
  const repById = new Map((reps || []).map((rep) => [String(rep.id || '').trim(), rep]));
  const map = new Map<string, SalesRepPerformanceRow>();

  rows.forEach((invoice) => {
    const key = String(invoice.salesRepId || '').trim() || 'unassigned';
    const rep = repById.get(key);
    const current = map.get(key) || {
      salesRepId: key,
      salesRepName: rep ? String(rep.name || '').trim() : key === 'unassigned' ? 'غير محدد' : key,
      invoiceCount: 0,
      totalSales: 0,
      averageInvoice: 0,
    };

    current.invoiceCount += 1;
    current.totalSales += toNumber(invoice.grandTotal);
    current.averageInvoice = current.invoiceCount > 0 ? current.totalSales / current.invoiceCount : 0;
    map.set(key, current);
  });

  return Array.from(map.values()).sort((left, right) => right.totalSales - left.totalSales);
}

export function buildInventoryTurnover(invoices: SalesInvoice[], materials: MaterialLike[], movements: InventoryMovement[], filters: ReportFilters): InventoryTurnover {
  const rows = filteredSalesInvoices(invoices, filters).filter((invoice) => String(invoice.type || 'sale') !== 'return');
  const materialsById = materialIndex(materials);

  const cogsEstimate = rows.reduce((sum, invoice) => {
    const invoiceCost = (invoice.items || []).reduce((invoiceSum, item) => {
      const unitCost = toNumber(materialsById.get(String(item.materialId || '').trim())?.purchasePrice);
      return invoiceSum + toNumber(item.quantity) * unitCost;
    }, 0);
    return sum + invoiceCost;
  }, 0);

  const allMovements = movements || [];
  const openingMovements = allMovements.filter((movement) => {
    const d = normDate(movement.date);
    const from = normDate(filters.fromDate);
    return from ? d < from : false;
  });
  const closingMovements = filteredMovements(allMovements, filters);

  const openingStockValue = buildInventoryCostReport(openingMovements, materials, {}).reduce((sum, row) => sum + row.stockValue, 0);
  const closingStockValue = buildInventoryCostReport(closingMovements, materials, {}).reduce((sum, row) => sum + row.stockValue, 0);
  const averageStockValue = (openingStockValue + closingStockValue) / 2;
  const turnoverRate = averageStockValue > 0 ? cogsEstimate / averageStockValue : 0;

  return {
    cogsEstimate,
    openingStockValue,
    closingStockValue,
    averageStockValue,
    turnoverRate,
  };
}

export function buildReorderReport(movements: InventoryMovement[], materials: MaterialLike[], filters: ReportFilters): ReorderRow[] {
  const stockRows = buildCurrentStock(movements, materials, filters);
  const byId = new Map(stockRows.map((row) => [row.materialId, row]));

  return (materials || [])
    .map((material) => {
      const id = String(material.id || '').trim();
      const currentQty = toNumber(byId.get(id)?.balanceQty);
      const reorderMinimumQty = toNumber(material.reorderMinimumQty);
      return {
        materialId: id,
        materialName: String(material.name || '').trim() || id,
        currentQty,
        reorderMinimumQty,
        shortageQty: Math.max(0, reorderMinimumQty - currentQty),
      };
    })
    .filter((row) => row.reorderMinimumQty > 0)
    .filter((row) => row.currentQty <= row.reorderMinimumQty)
    .sort((left, right) => right.shortageQty - left.shortageQty);
}

export function buildReturnsReport(invoices: SalesInvoice[], filters: ReportFilters): ReturnsRow[] {
  return filteredSalesInvoices(invoices, filters)
    .filter((invoice) => String(invoice.type || '').trim() === 'return')
    .map((invoice) => ({
      invoiceId: String(invoice.id || '').trim(),
      invoiceNumber: String(invoice.invoiceNumber || '').trim(),
      date: normDate(invoice.date),
      customerId: String(invoice.customerId || '').trim(),
      customerName: String(invoice.customerName || '').trim(),
      returnValue: toNumber(invoice.grandTotal),
    }))
    .sort((left, right) => normDate(right.date).localeCompare(normDate(left.date)));
}

export function buildPosShiftReport(
  sessions: PosShiftSessionLike[],
  filters: Pick<ReportFilters, 'fromDate' | 'toDate'>,
  userId?: string,
): PosShiftReport {
  const normalizedUserId = String(userId || '').trim();

  const normalizedSessions: PosShiftRow[] = (sessions || [])
    .map((entry, index) => {
      const startedAt = String(entry?.startedAt || '').trim();
      const endedAt = String(entry?.endedAt || '').trim();
      const id = String(entry?.id || '').trim() || `shift-${index}`;
      const shiftUserId = String(entry?.userId || '').trim() || 'unknown';
      const shiftUserName = String(entry?.userName || '').trim() || shiftUserId;
      if (!startedAt) return null;

      return {
        id,
        userId: shiftUserId,
        userName: shiftUserName,
        startedAt,
        endedAt,
        status: entry?.status === 'closed' ? 'closed' : 'active',
        openingCash: toNumber(entry?.openingCash),
        openingVisa: toNumber(entry?.openingVisa),
        totalSalesAmount: toNumber(entry?.totalSalesAmount),
        invoicesCount: Math.max(0, toNumber(entry?.invoicesCount)),
        returnsCount: Math.max(0, toNumber(entry?.returnsCount)),
        cashSalesAmount: toNumber(entry?.cashSalesAmount),
        visaSalesAmount: toNumber(entry?.visaSalesAmount),
        receivablesAmount: toNumber(entry?.receivablesAmount),
        couponAmount: toNumber(entry?.couponAmount),
        expectedCashAtClose: toNumber(entry?.expectedCashAtClose),
        expectedVisaAtClose: toNumber(entry?.expectedVisaAtClose),
        countedCashAtClose: toNumber(entry?.countedCashAtClose),
        countedVisaAtClose: toNumber(entry?.countedVisaAtClose),
        cashVariance: toNumber(entry?.cashVariance),
        visaVariance: toNumber(entry?.visaVariance),
        requiresApproval: entry?.requiresApproval === true,
        cashVarianceHandled: entry?.cashVarianceHandled === true,
      } as PosShiftRow;
    })
    .filter((entry): entry is PosShiftRow => !!entry)
    .filter((entry) => withinDateTimeRange(entry.startedAt, filters.fromDate, filters.toDate))
    .filter((entry) => !normalizedUserId || entry.userId === normalizedUserId)
    .sort((left, right) => String(right.startedAt).localeCompare(String(left.startedAt)));

  const summaryMap = new Map<string, PosShiftUserSummaryRow>();
  normalizedSessions.forEach((entry) => {
    const current = summaryMap.get(entry.userId) || {
      userId: entry.userId,
      userName: entry.userName,
      shiftsCount: 0,
      activeShiftsCount: 0,
      closedShiftsCount: 0,
      invoicesCount: 0,
      returnsCount: 0,
      totalSalesAmount: 0,
      cashSalesAmount: 0,
      visaSalesAmount: 0,
      receivablesAmount: 0,
      couponAmount: 0,
      openingCash: 0,
      openingVisa: 0,
      cashVariance: 0,
      visaVariance: 0,
    };
    current.shiftsCount += 1;
    current.activeShiftsCount += entry.status === 'active' ? 1 : 0;
    current.closedShiftsCount += entry.status === 'closed' ? 1 : 0;
    current.invoicesCount += entry.invoicesCount;
    current.returnsCount += entry.returnsCount;
    current.totalSalesAmount += entry.totalSalesAmount;
    current.cashSalesAmount += entry.cashSalesAmount;
    current.visaSalesAmount += entry.visaSalesAmount;
    current.receivablesAmount += entry.receivablesAmount;
    current.couponAmount += entry.couponAmount;
    current.openingCash += entry.openingCash;
    current.openingVisa += entry.openingVisa;
    current.cashVariance += entry.cashVariance;
    current.visaVariance += entry.visaVariance;
    summaryMap.set(entry.userId, current);
  });

  const summaryByUser = Array.from(summaryMap.values()).sort((left, right) => right.totalSalesAmount - left.totalSalesAmount);

  const totals = {
    shiftsCount: normalizedSessions.length,
    activeShiftsCount: normalizedSessions.filter((entry) => entry.status === 'active').length,
    closedShiftsCount: normalizedSessions.filter((entry) => entry.status === 'closed').length,
    invoicesCount: normalizedSessions.reduce((sum, entry) => sum + entry.invoicesCount, 0),
    returnsCount: normalizedSessions.reduce((sum, entry) => sum + entry.returnsCount, 0),
    totalSalesAmount: normalizedSessions.reduce((sum, entry) => sum + entry.totalSalesAmount, 0),
    cashVariance: normalizedSessions.reduce((sum, entry) => sum + entry.cashVariance, 0),
    visaVariance: normalizedSessions.reduce((sum, entry) => sum + entry.visaVariance, 0),
  };

  return {
    sessions: normalizedSessions,
    summaryByUser,
    totals,
  };
}
