/**
 * Sales Invoice Server Actions
 * Server-side entry points for creating, updating, and managing sales invoices
 * Called from client components via Next.js Server Actions
 */

'use server';

import { getSession } from '@/lib/auth';
import { headers } from 'next/headers';
import { verifyActionGuardToken } from '@/lib/action-guard';
import { checkRateLimit } from '@/lib/rate-limiter';
import { getSalesInvoiceService } from '@/lib/modules/sales';
import { syncRealEstateCollectionsFromSalesReceipts } from '@/lib/actions';

import {
  pgAddPayment,
  pgGetPayments,
  pgUpdatePayment,
  pgCancelPayment,
  pgGetDefaultCurrency,
  pgGetGeneralBanks,
  pgGetMaterials,
  pgGetSettings,
  pgSaveInventoryMovements,
  pgAddIncomingCheck,
  pgMarkPendingIncomingChecksBySourceTagAsReturned,
  pgRemovePendingIncomingChecksBySourceTag,
  pgAcquireSupplierPaymentLock,
  pgReleaseSupplierPaymentLock,
  pgNextNamedSequence,
  pgGetExchangeRate,
} from '@/lib/postgres/data-access';
import type {
  SalesInvoice,
  CreateSalesInvoiceInput,
  SalesInvoicePaymentMethod,
} from '@/lib/modules/sales';
import type { UpdateSalesInvoiceInput } from '@/lib/modules/sales/application/sales-invoice-application-service';

async function ensurePostedSalesInvoiceJournalEntry(params: {
  invoice: SalesInvoice;
  createdBy: string;
}) {
  const { DefaultSalesAccountingPort } = await import('@/lib/modules/sales/infrastructure/default-sales-accounting.port');
  const accounting = new DefaultSalesAccountingPort();
  await accounting.createInvoiceEntry({
    invoiceId: params.invoice.id,
    invoiceNumber: params.invoice.invoiceNumber,
    date: params.invoice.date,
    amount: Number(params.invoice.grandTotal || 0),
    baseCurrencyAmount: Number(params.invoice.grandTotalBase || 0) || undefined,
    taxAmount: Number(params.invoice.taxAmount || 0),
    discountAmount: Number(params.invoice.totalDiscount || 0),
    exchangeRate: Number(params.invoice.exchangeRate || 1),
    description: `Sales invoice ${params.invoice.invoiceNumber}`,
    customerId: params.invoice.customerId,
    customerName: params.invoice.customerName,
    secondaryCustomerId: params.invoice.secondaryCustomerId,
    secondaryCustomerName: params.invoice.secondaryCustomerName,
    primaryCoveragePercent: params.invoice.primaryCoveragePercent,
    secondaryCoveragePercent: params.invoice.secondaryCoveragePercent,
    primaryCoverageAmount: params.invoice.primaryCoverageAmount,
    secondaryCoverageAmount: params.invoice.secondaryCoverageAmount,
    paymentMethods: params.invoice.paymentMethods,
    status: 'posted',
    createdBy: params.createdBy,
    documentType: params.invoice.documentType === 'shipment' ? 'shipment' : 'tax_invoice',
  });
}

async function ensureShipmentDraftJournalEntry(params: {
  invoice: SalesInvoice;
  createdBy: string;
}) {
  const { DefaultSalesAccountingPort } = await import('@/lib/modules/sales/infrastructure/default-sales-accounting.port');
  const accounting = new DefaultSalesAccountingPort();
  await accounting.createInvoiceEntry({
    invoiceId: params.invoice.id,
    invoiceNumber: params.invoice.invoiceNumber,
    date: params.invoice.date,
    amount: Number(params.invoice.grandTotal || 0),
    baseCurrencyAmount: Number(params.invoice.grandTotalBase || 0) || undefined,
    taxAmount: Number(params.invoice.taxAmount || 0),
    discountAmount: Number(params.invoice.totalDiscount || 0),
    exchangeRate: Number(params.invoice.exchangeRate || 1),
    description: `Shipment draft ${params.invoice.invoiceNumber}`,
    customerId: params.invoice.customerId,
    customerName: params.invoice.customerName,
    paymentMethods: params.invoice.paymentMethods,
    status: 'draft',
    createdBy: params.createdBy,
    documentType: 'shipment',
  });
}

async function promoteLinkedShipmentDraftJournals(params: {
  invoice: SalesInvoice;
}) {
  const shipmentIds = Array.isArray((params.invoice as any).linkedShipmentIds)
    ? ((params.invoice as any).linkedShipmentIds as string[])
    : [];
  if (shipmentIds.length === 0) return;

  const { DefaultSalesAccountingPort } = await import('@/lib/modules/sales/infrastructure/default-sales-accounting.port');
  const accounting = new DefaultSalesAccountingPort();
  for (const shipmentId of shipmentIds) {
    const normalizedShipmentId = String(shipmentId || '').trim();
    if (!normalizedShipmentId) continue;
    await accounting.postInvoice(normalizedShipmentId, params.invoice.invoiceNumber);
  }
}

async function getStoredExchangeRate(fromCurrency: string, toCurrency: string): Promise<number> {
  const normalizedFrom = String(fromCurrency || '').trim() || 'JOD';
  const normalizedTo = String(toCurrency || '').trim() || normalizedFrom;
  if (normalizedFrom === normalizedTo) {
    return 1;
  }

  const rawRate = await pgGetExchangeRate(normalizedFrom, normalizedTo);
  const numericRate = Number(rawRate || 0);
  return Number.isFinite(numericRate) && numericRate > 0 ? numericRate : 1;
}

function buildSalesInvoiceChecksSourceTag(invoiceId: string) {
  return `[AUTO_SALES_INVOICE:${String(invoiceId || '').trim()}]`;
}

function buildSalesReceiptChecksSourceTag(receiptId: string) {
  return `[AUTO_SALES_RECEIPT:${String(receiptId || '').trim()}]`;
}

const SALES_RECEIPT_REFERENCE_PATTERN = /^RCP\/\d{4}\/\d{4}$/i;

function formatSalesReceiptReference(year: number, sequence: number) {
  return `RCP/${year}/${String(sequence).padStart(4, '0')}`;
}

async function allocateSalesReceiptReference(dateIso: string): Promise<string> {
  const year = new Date(dateIso).getUTCFullYear();
  const sequenceKey = `sales_receipt_ref_seq_${year}`;
  const nextValue = await pgNextNamedSequence(sequenceKey);  return formatSalesReceiptReference(year, nextValue);
}

async function validateManualSalesReceiptReference(params: {
  reference?: string;
  ignorePaymentId?: string;
}): Promise<{ success: true; normalizedReference?: string } | { success: false; error: string }> {
  const normalizedReference = String(params.reference || '').trim();
  if (!normalizedReference) {
    return { success: true, normalizedReference: undefined };
  }

  if (!SALES_RECEIPT_REFERENCE_PATTERN.test(normalizedReference)) {
    return { success: true, normalizedReference };
  }

  const comparableReference = normalizedReference.toUpperCase();
  const ignoredPaymentId = String(params.ignorePaymentId || '').trim();
  const rows = (await pgGetPayments({ page: 1, pageSize: 5000 })).items;
  const duplicate = (rows || []).find((row: any) => {
    const currentId = String(row?.id || '').trim();
    if (ignoredPaymentId && currentId === ignoredPaymentId) return false;
    const currentReference = String(row?.reference || '').trim().toUpperCase();
    return currentReference === comparableReference;
  });

  if (duplicate) {
    return {
      success: false,
      error: `Reference ${comparableReference} is already used by receipt ${String((duplicate as any)?.id || '').trim()}.`,
    };
  }

  return { success: true, normalizedReference: comparableReference };
}

function buildSalesReceiptCustomerLockKey(customerId: string) {
  return `sales-receipt:customer:${String(customerId || '').trim()}`;
}

function buildSalesReceiptInvoiceLockKey(invoiceId: string) {
  return `sales-receipt:invoice:${String(invoiceId || '').trim()}`;
}

function buildSalesReceiptLockKeys(customerId: string, invoiceIds: string[]) {
  const keys = [buildSalesReceiptCustomerLockKey(customerId)];
  for (const invoiceId of invoiceIds) {
    const normalized = String(invoiceId || '').trim();
    if (!normalized) continue;
    keys.push(buildSalesReceiptInvoiceLockKey(normalized));
  }
  return Array.from(new Set(keys.filter(Boolean)));
}

async function acquireSalesReceiptLocks(params: {
  customerId: string;
  invoiceIds: string[];
  userId: string;
  userName: string;
}) {
  const keys = buildSalesReceiptLockKeys(params.customerId, params.invoiceIds);
  const acquired: string[] = [];

  for (const key of keys) {
    const result = await pgAcquireSupplierPaymentLock(key, params.userId, params.userName);
    if (!result?.success) {
      for (const acquiredKey of acquired) {
        
        await pgReleaseSupplierPaymentLock(acquiredKey, params.userId);
        
      }

      return {
        success: false as const,
        error: `Cannot continue because this customer/invoice is being processed by ${String(result?.lockedBy || 'another user')}.`,
      };
    }

    acquired.push(key);
  }

  return { success: true as const, acquiredKeys: acquired };
}

async function releaseSalesReceiptLocks(acquiredKeys: string[], userId: string) {
  for (const key of acquiredKeys) {
    
    await pgReleaseSupplierPaymentLock(key, userId);
    
  }
}

async function resolveBankMeta(params: { bankId?: string; bankName?: string }): Promise<{ bankId: string; bankName: string } | null> {
  const normalizedBankId = String(params.bankId || '').trim();
  const normalizedBankName = String(params.bankName || '').trim();

  const rows = await pgGetGeneralBanks();
  if (normalizedBankId) {
    const byId = (rows || []).find((entry: any) => String(entry.id || '') === normalizedBankId);
    return {
      bankId: normalizedBankId,
      bankName: String(byId?.name || normalizedBankName || normalizedBankId),
    };
  }

  if (normalizedBankName) {
    const byName = (rows || []).find(
      (entry: any) => String(entry.name || '').trim().toLowerCase() === normalizedBankName.toLowerCase()
    );
    return {
      bankId: String(byName?.id || normalizedBankName),
      bankName: String(byName?.name || normalizedBankName),
    };
  }

  return null;
}

async function syncIncomingCheckForReceipt(params: {
  receiptId: string;
  method: SalesInvoicePaymentMethod['method'];
  postingStatus?: 'draft' | 'saved' | 'posted';
  customerId: string;
  customerName: string;
  amount: number;
  currencyCode: string;
  exchangeRate: number;
  receiptChecks?: Array<{
    checkNumber?: string;
    bankId?: string;
    bankName?: string;
    checkDate?: string;
    amount?: number;
    currency?: string;
    reference?: string;
    status?: 'pending' | 'cleared' | 'rejected' | 'returned';
  }>;
  checkNumber?: string;
  bankId?: string;
  checkDate?: string;
  reference?: string;
  createdBy: string;
  noteSuffix?: string;
}) {
  const sourceTag = buildSalesReceiptChecksSourceTag(params.receiptId);

  
  await pgRemovePendingIncomingChecksBySourceTag(sourceTag);
  

  const shouldCreateCheck = params.postingStatus === 'posted' && params.method === 'check';
  if (!shouldCreateCheck) return;

  const explicitChecks = (params.receiptChecks || [])
    .map((row) => ({
      checkNumber: String(row.checkNumber || '').trim(),
      bankId: String(row.bankId || '').trim(),
      bankName: String(row.bankName || '').trim(),
      checkDate: String(row.checkDate || '').trim(),
      amount: Number(row.amount || 0),
      currency: String(row.currency || params.currencyCode || 'JOD').trim() || 'JOD',
      reference: String(row.reference || params.reference || '').trim(),
      status: (String(row.status || 'pending').trim() as 'pending' | 'cleared' | 'rejected' | 'returned') || 'pending',
    }))
    .filter((row) => row.checkNumber && row.checkDate && Number.isFinite(row.amount) && row.amount > 0);

  const fallbackCheck = {
    checkNumber: String(params.checkNumber || '').trim(),
    bankId: String(params.bankId || '').trim(),
    bankName: '',
    checkDate: String(params.checkDate || '').trim(),
    amount: Number(params.amount || 0),
    currency: String(params.currencyCode || 'JOD').trim() || 'JOD',
    reference: String(params.reference || '').trim(),
    status: 'pending' as const,
  };

  const checksToCreate = explicitChecks.length > 0
    ? explicitChecks
    : (fallbackCheck.checkNumber && fallbackCheck.checkDate && fallbackCheck.amount > 0 ? [fallbackCheck] : []);

  const baseCurrencyCode = await getBaseCurrencyCode();
  for (const check of checksToCreate) {
    const bankMeta = await resolveBankMeta({ bankId: check.bankId, bankName: check.bankName });
    if (!bankMeta) continue;

    const checkCurrency = String((check as any).currency || params.currencyCode || 'JOD').trim() || 'JOD';
    const checkExchangeRate = await getStoredExchangeRate(checkCurrency, baseCurrencyCode);

    const payload = {
      checkNumber: check.checkNumber,
      bankId: bankMeta.bankId,
      bankName: bankMeta.bankName,
      checkDate: check.checkDate,
      amount: check.amount,
      currency: checkCurrency,
      convertedAmount: check.amount * checkExchangeRate,
      exchangeRate: checkExchangeRate,
      customerId: params.customerId,
      customerName: params.customerName,
      reference: String((check as any).reference || params.reference || params.receiptId),
      status: ((check as any).status || 'pending') as 'pending' | 'cleared' | 'rejected' | 'returned',
      notes: `${sourceTag}${params.noteSuffix ? ` ${params.noteSuffix}` : ''}`,
      createdBy: params.createdBy,
    };

    
    await pgAddIncomingCheck(payload);
    
  }
}

async function markReceiptIncomingChecksAsReturned(receiptId: string, reason?: string) {
  const sourceTag = buildSalesReceiptChecksSourceTag(receiptId);
  
  await pgMarkPendingIncomingChecksBySourceTagAsReturned(sourceTag, reason);
}

async function syncIncomingChecksForInvoice(params: {
  invoiceId: string;
  invoiceNumber: string;
  customerId: string;
  customerName: string;
  date: string;
  exchangeRate?: number;
  currencyCode?: string;
  paymentMethods?: Array<SalesInvoicePaymentMethod>;
  createdBy: string;
}) {
  const {
    invoiceId,
    invoiceNumber,
    customerId,
    customerName,
    date,
    exchangeRate,
    currencyCode,
    paymentMethods,
    createdBy,
  } = params;

  const sourceTag = buildSalesInvoiceChecksSourceTag(invoiceId);

  
  await pgRemovePendingIncomingChecksBySourceTag(sourceTag);
  

  const checkMethods = (paymentMethods || []).filter(
    (method) => method.method === 'check' && method.checkNumber && method.checkDate && method.bankId
  );

  for (const method of checkMethods) {
    const amount = Number(method.amount || 0);
    if (amount <= 0) continue;

    const rate = Number(exchangeRate || 1) || 1;
    const currency = String(method.currency || currencyCode || 'USD');
    const payload = {
      checkNumber: String(method.checkNumber || ''),
      bankId: String(method.bankId || ''),
      bankName: String(method.bankName || method.bankId || ''),
      checkDate: String(method.checkDate || date),
      amount,
      currency,
      convertedAmount: amount * rate,
      exchangeRate: rate,
      customerId,
      customerName,
      reference: String(method.reference || invoiceNumber || ''),
      status: method.status || 'pending',
      notes: `${sourceTag} Sales invoice ${invoiceNumber}`,
      createdBy,
    };

    
    await pgAddIncomingCheck(payload);
    
  }
}

async function clearIncomingChecksForInvoice(invoiceId: string) {
  const sourceTag = buildSalesInvoiceChecksSourceTag(invoiceId);
  
  await pgRemovePendingIncomingChecksBySourceTag(sourceTag);
}

async function syncInventoryIssueForPostedInvoice(invoice: SalesInvoice): Promise<void> {
  const settings = await pgGetSettings();
  const warehousesEnabled = settings?.warehousesEnabled === true || settings?.warehousesEnabled === 'true';
  if (!warehousesEnabled) return;

  const materialsRows = (await pgGetMaterials({ page: 1, pageSize: 5000 })).items || [];
  const materialCategoryById = new Map<string, string>();
  for (const row of materialsRows as Array<any>) {
    const id = String(row?.id || '').trim();
    if (!id) continue;
    materialCategoryById.set(id, String(row?.itemCategory || '').trim().toLowerCase());
  }

  const movementDate = String(invoice.date || new Date().toISOString()).trim() || new Date().toISOString();
  const defaultWarehouseId = String((invoice as any).warehouseId || '').trim();

  const movements = (invoice.items || []).flatMap((item, index) => {
    const materialId = String(item?.materialId || '').trim();
    const quantity = Number(item?.quantity || 0);
    if (!materialId || !Number.isFinite(quantity) || quantity <= 0) return [];

    const category = materialCategoryById.get(materialId);
    if (category && category !== 'inventory') return [];

    const warehouseId = String((item as any)?.warehouseId || defaultWarehouseId || '').trim();
    const shelfId = String((item as any)?.shelfId || '').trim();
    if (!warehouseId || !shelfId) {
      throw new Error(`MISSING_STOCK_LOCATION_FOR_SALES_ITEM:${materialId}`);
    }

    const stableId = `inv-sale-${String(invoice.id || '').trim()}-${index}`;
    return [{
      id: stableId,
      date: movementDate,
      materialId,
      warehouseId,
      shelfId,
      quantityIn: 0,
      quantityOut: quantity,
      referenceType: 'sale',
      referenceId: String(invoice.id || ''),
      invoiceId: String(invoice.id || ''),
      note: `Sales invoice ${String(invoice.invoiceNumber || invoice.id || '')}`,
    }];
  });

  if (movements.length > 0) {
    await pgSaveInventoryMovements(movements as Array<Record<string, unknown>>);
  }
}

async function hasValidSalesActionGuard(input: any) {
  const submittedGuard = String(input?.__salesActionGuard || '').trim();
  if (!submittedGuard) return false;

  const session = await getSession();
  if (!session?.user?.id) return false;

  return verifyActionGuardToken({
    token: submittedGuard,
    scope: 'sales',
    userId: session.user.id,
  });
}

/**
 * Create a new sales invoice
 * Called from: SalesInvoiceForm component
 */
export async function handleCreateSalesInvoice(
  input: Omit<CreateSalesInvoiceInput, 'userId' | 'userName'> & {
    userId?: string;
    userName?: string;
  }
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    if (!(await hasValidSalesActionGuard(input))) {
      if (process.env.NODE_ENV !== 'production') {
        console.warn('[Sales Action] rejected-invalid-guard', {
          timestamp: new Date().toISOString(),
        });
      }
      return { success: false, error: 'INVALID_SALES_ACTION_SIGNATURE' };
    }

    const createSalesHeaders = await headers();
    const createSalesIp = createSalesHeaders.get('x-forwarded-for') ?? createSalesHeaders.get('x-real-ip') ?? 'unknown';
    if (!checkRateLimit(createSalesIp, 'sales')) {
      return { success: false, error: 'RATE_LIMIT_EXCEEDED' };
    }

    const service = getSalesInvoiceService();

    const result = await service.create({
      ...input,
      userId: input.userId || 'unknown',
      userName: input.userName || 'Unknown User',
    });

    const isShipmentDoc = (result as any).documentType === 'shipment';
    // Shipments apply inventory at 'saved' or 'posted'; tax invoices only at 'posted'
    const shouldApplyInventory = isShipmentDoc
      ? (result.postingStatus === 'saved' || result.postingStatus === 'posted')
      : result.postingStatus === 'posted';

    if (shouldApplyInventory) {
      try {
        await syncInventoryIssueForPostedInvoice(result);
      } catch (inventoryErr) {
        console.error('[Sales Action] Inventory movement creation failed:', inventoryErr);
        return {
          success: false,
          error: inventoryErr instanceof Error ? inventoryErr.message : 'INVENTORY_MOVEMENT_FAILED',
        };
      }
    }

    if (isShipmentDoc && (result.postingStatus === 'saved' || result.postingStatus === 'posted')) {
      try {
        await ensureShipmentDraftJournalEntry({
          invoice: result,
          createdBy: input.userName || 'Unknown User',
        });
      } catch (jeErr) {
        console.error('[Sales Action] Shipment draft journal creation failed (non-fatal):', jeErr);
      }
    }

    if (result.postingStatus === 'posted' && !isShipmentDoc) {
      try {
        await promoteLinkedShipmentDraftJournals({
          invoice: result,
        });
      } catch (promotionErr) {
        console.error('[Sales Action] Shipment draft promotion failed (non-fatal):', promotionErr);
      }

      try {
        await ensurePostedSalesInvoiceJournalEntry({
          invoice: result,
          createdBy: input.userName || 'Unknown User',
        });
      } catch (jeErr) {
        console.error('[Sales Action] Journal entry creation failed (non-fatal):', jeErr);
      }
    }
    if (result.postingStatus === 'posted') {
      try {
        await syncIncomingChecksForInvoice({
          invoiceId: result.id,
          invoiceNumber: result.invoiceNumber,
          customerId: result.customerId,
          customerName: result.customerName,
          date: input.date,
          exchangeRate: input.exchangeRate,
          currencyCode: input.currencyCode,
          paymentMethods: input.paymentMethods,
          createdBy: input.userName || 'Unknown User',
        });
      } catch (syncErr) {
        console.error('[Sales Action] Check sync failed (non-fatal):', syncErr);
      }
    } else {
      try {
        await clearIncomingChecksForInvoice(result.id);
      } catch (clearErr) {
        console.error('[Sales Action] Check clear failed (non-fatal):', clearErr);
      }
    }

    return { success: true, data: result };
  } catch (error) {
    console.error('Failed to create sales invoice:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Update an existing sales invoice
 */
export async function handleUpdateSalesInvoice(
  id: string,
  input: UpdateSalesInvoiceInput
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    if (!(await hasValidSalesActionGuard(input))) {
      if (process.env.NODE_ENV !== 'production') {
        console.warn('[Sales Action] rejected-invalid-guard', {
          invoiceId: String(id || ''),
          timestamp: new Date().toISOString(),
        });
      }
      return { success: false, error: 'INVALID_SALES_ACTION_SIGNATURE' };
    }

    const updateSalesHeaders = await headers();
    const updateSalesIp = updateSalesHeaders.get('x-forwarded-for') ?? updateSalesHeaders.get('x-real-ip') ?? 'unknown';
    if (!checkRateLimit(updateSalesIp, 'sales')) {
      return { success: false, error: 'RATE_LIMIT_EXCEEDED' };
    }

    const service = getSalesInvoiceService();
    const existing = await service.getById(id);
    if (!existing) {
      return { success: false, error: 'Invoice not found' };
    }

    const result = await service.updateInvoice(id, input);

    const isShipmentDoc = result.documentType === 'shipment';

    if (isShipmentDoc && (result.postingStatus === 'saved' || result.postingStatus === 'posted')) {
      try {
        await ensureShipmentDraftJournalEntry({
          invoice: result,
          createdBy: input.userName || 'system',
        });
      } catch (jeErr) {
        console.error('[Sales Action] Shipment draft journal update failed (non-fatal):', jeErr);
      }
    }

    if (result.postingStatus === 'posted' && !isShipmentDoc) {
      try {
        await promoteLinkedShipmentDraftJournals({
          invoice: result,
        });
      } catch (promotionErr) {
        console.error('[Sales Action] Shipment draft promotion failed on update (non-fatal):', promotionErr);
      }

      try {
        await ensurePostedSalesInvoiceJournalEntry({
          invoice: result,
          createdBy: input.userName || 'system',
        });
      } catch (jeErr) {
        console.error('[Sales Action] Journal entry update failed (non-fatal):', jeErr);
      }
    }

    if (result.postingStatus === 'posted') {
      try {
        await syncIncomingChecksForInvoice({
          invoiceId: result.id,
          invoiceNumber: result.invoiceNumber,
          customerId: result.customerId,
          customerName: result.customerName,
          date: result.date,
          exchangeRate: result.exchangeRate,
          currencyCode: result.currencyCode,
          paymentMethods: result.paymentMethods,
          createdBy: 'system',
        });
      } catch (syncErr) {
        console.error('[Sales Action] Check sync failed on update (non-fatal):', syncErr);
      }
    } else {
      try {
        await clearIncomingChecksForInvoice(result.id);
      } catch (clearErr) {
        console.error('[Sales Action] Check clear failed on update (non-fatal):', clearErr);
      }
    }

    return { success: true, data: result };
  } catch (error) {
    console.error('Failed to update sales invoice:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Get invoice by ID
 */
export async function handleGetSalesInvoice(
  id: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const invoice = await service.getById(id);

    if (!invoice) {
      return { success: false, error: 'Invoice not found' };
    }

    return { success: true, data: invoice };
  } catch (error) {
    console.error('Failed to get sales invoice:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * List invoices with filtering
 */
export async function handleListSalesInvoices(options?: {
  skip?: number;
  take?: number;
  status?: string;
  documentType?: 'shipment' | 'tax_invoice';
  customerId?: string;
  search?: string;
  excludeShipments?: boolean;
}): Promise<{
  success: boolean;
  data?: { items: SalesInvoice[]; total: number };
  error?: string;
}> {
  try {
    const service = getSalesInvoiceService();
    const result = await service.list(options);

    return { success: true, data: result };
  } catch (error) {
    console.error('Failed to list sales invoices:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Fetch shipments (document_type='shipment') for a customer that have not yet
 * been linked to a tax invoice. Used to populate the "link shipments" picker
 * when creating a tax invoice.
 */
export async function handleGetUnlinkedShipmentsForCustomer(
  customerId: string
): Promise<{
  success: boolean;
  data?: {
    id: string;
    invoiceNumber: string;
    date: string;
    grandTotal: number;
    currencyCode?: string;
    items?: {
      materialId?: string;
      quantity: number;
      unitPrice: number;
      lineDiscountPercent?: number;
      lineDiscountAmount?: number;
      taxRate?: number;
      shelfId?: string;
      description?: string;
      name?: string;
      realEstateProjectId?: string;
      realEstateProjectName?: string;
      realEstateUnitId?: string;
      realEstateUnitName?: string;
    }[];
  }[];
  error?: string;
}> {
  try {
    const service = getSalesInvoiceService();
    const result = await service.list({ customerId, documentType: 'shipment', take: 500 });
    const unlinked = (result.items || []).filter((inv: any) => !inv.taxInvoiceId);
    return {
      success: true,
      data: unlinked.map((inv: any) => ({
        id: inv.id,
        invoiceNumber: inv.invoiceNumber,
        date: inv.date,
        grandTotal: inv.grandTotal,
        currencyCode: inv.currencyCode,
        items: Array.isArray(inv.items)
          ? inv.items.map((item: any) => ({
              materialId: String(item.materialId || '').trim(),
              quantity: Number(item.quantity || 0),
              unitPrice: Number(item.unitPrice || 0),
              lineDiscountPercent: Number(item.lineDiscountPercent || 0),
              lineDiscountAmount: Number(item.lineDiscountAmount || 0),
              taxRate: Number(item.taxRate || 0),
              shelfId: String(item.shelfId || '').trim() || undefined,
              description: String(item.description || '').trim() || undefined,
              name: String(item.name || '').trim() || undefined,
              realEstateProjectId: String(item.realEstateProjectId || '').trim() || undefined,
              realEstateProjectName: String(item.realEstateProjectName || '').trim() || undefined,
              realEstateUnitId: String(item.realEstateUnitId || '').trim() || undefined,
              realEstateUnitName: String(item.realEstateUnitName || '').trim() || undefined,
            }))
          : [],
      })),
    };
  } catch (error) {
    console.error('Failed to fetch unlinked shipments:', error);
    return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
  }
}

/**
 * Backfill accounting journals for posted sales invoices.
 * Useful for repairing legacy invoices that were marked posted but missing journal entries.
 */
export async function handleBackfillPostedSalesInvoiceJournals(): Promise<{
  success: boolean;
  data?: {
    totalScanned: number;
    totalPostedEligible: number;
    totalRebuilt: number;
    totalFailed: number;
    failedInvoices: string[];
  };
  error?: string;
}> {
  try {
    const session = await getSession();
    if (!session?.user || session.user.role !== 'admin') {
      return { success: false, error: 'Unauthorized' };
    }

    const service = getSalesInvoiceService();
    const pageSize = 500;
    let skip = 0;
    let totalScanned = 0;
    let totalPostedEligible = 0;
    let totalRebuilt = 0;
    let totalFailed = 0;
    const failedInvoices: string[] = [];

    while (true) {
      const batch = await service.list({ skip, take: pageSize });
      const items = Array.isArray(batch.items) ? batch.items : [];
      if (items.length === 0) break;

      totalScanned += items.length;

      for (const invoice of items) {
        const postingStatus = String(invoice.postingStatus || 'saved');
        const status = String(invoice.status || 'active');
        const isShipmentDoc = String((invoice as any).documentType || '') === 'shipment';
        if (postingStatus !== 'posted' || status === 'cancelled' || isShipmentDoc) {
          continue;
        }

        totalPostedEligible += 1;
        try {
          await ensurePostedSalesInvoiceJournalEntry({
            invoice,
            createdBy: session.user.name || 'admin',
          });
          totalRebuilt += 1;
        } catch (error) {
          totalFailed += 1;
          failedInvoices.push(String(invoice.invoiceNumber || invoice.id || 'unknown'));
          console.error('Failed to backfill sales invoice journal:', {
            invoiceId: invoice.id,
            invoiceNumber: invoice.invoiceNumber,
            error,
          });
        }
      }

      if (items.length < pageSize) break;
      skip += pageSize;
    }

    return {
      success: true,
      data: {
        totalScanned,
        totalPostedEligible,
        totalRebuilt,
        totalFailed,
        failedInvoices,
      },
    };
  } catch (error) {
    console.error('Failed to backfill posted sales invoice journals:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Lock invoice for editing
 */
export async function handleLockSalesInvoice(
  id: string,
  userId: string,
  userName: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const invoice = await service.lock(id, userId, userName);

    return { success: true, data: invoice };
  } catch (error) {
    console.error('Failed to lock sales invoice:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Unlock invoice
 */
export async function handleUnlockSalesInvoice(
  id: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const invoice = await service.unlock(id);

    return { success: true, data: invoice };
  } catch (error) {
    console.error('Failed to unlock sales invoice:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Cancel invoice
 */
export async function handleCancelSalesInvoice(
  id: string,
  reason: string,
  userId: string,
  userName: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const invoice = await service.cancel(id, reason, userId, userName);

    const sourceTag = buildSalesInvoiceChecksSourceTag(invoice.id);
    
    await pgMarkPendingIncomingChecksBySourceTagAsReturned(sourceTag, reason || 'Invoice cancelled');
    

    return { success: true, data: invoice };
  } catch (error) {
    console.error('Failed to cancel sales invoice:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Mark invoice as shipped
 */
export async function handleMarkSalesInvoiceAsShipped(
  id: string,
  shipmentId: string,
  shipmentDate: string,
  userId: string,
  userName: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const invoice = await service.markAsShipped(
      id,
      shipmentId,
      shipmentDate,
      userId,
      userName
    );

    return { success: true, data: invoice };
  } catch (error) {
    console.error('Failed to mark sales invoice as shipped:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Record payment received
 */
export async function handleRecordSalesPayment(
  id: string,
  amount: number,
  method: SalesInvoicePaymentMethod,
  userId: string,
  userName: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const invoice = await service.recordPayment(id, amount, method, userId, userName);

    return { success: true, data: invoice };
  } catch (error) {
    console.error('Failed to record sales payment:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Undo last invoice update (when undo patch is available in activity log)
 */
export async function handleUndoLastSalesInvoiceUpdate(
  id: string,
  userId: string,
  userName: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const invoice = await service.getById(id);
    if (!invoice) {
      return { success: false, error: 'Invoice not found' };
    }

    if (invoice.status === 'cancelled' || invoice.postingStatus === 'posted' || invoice.shippingStatus === 'shipped' || invoice.shippingStatus === 'delivered') {
      return { success: false, error: 'لا يمكن التراجع عن فاتورة ملغاة/مرحلة/مشحونة.' };
    }

    const log = Array.isArray((invoice as any).activityLog) ? [...((invoice as any).activityLog as any[])] : [];
    log.sort((a, b) => new Date(String(b?.timestamp || 0)).getTime() - new Date(String(a?.timestamp || 0)).getTime());
    const lastUpdateEntry = log.find((entry) => String(entry?.action || '') === 'updated' && entry?.details?.undoPatch);

    if (!lastUpdateEntry || !lastUpdateEntry.details?.undoPatch) {
      return { success: false, error: 'لا يوجد تحديث سابق قابل للتراجع.' };
    }

    const undoPatch = lastUpdateEntry.details.undoPatch as UpdateSalesInvoiceInput;
    const restored = await service.updateInvoice(id, {
      ...undoPatch,
      userId,
      userName,
    });

    return { success: true, data: restored };
  } catch (error) {
    console.error('Failed to undo sales invoice update:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

type SalesPaymentReceiptInput = {
  paymentId?: string;
  ignoreReferencePaymentId?: string;
  customerId: string;
  amount: number;
  method: SalesInvoicePaymentMethod['method'];
  receiptChecks?: Array<{
    checkNumber?: string;
    bankId?: string;
    bankName?: string;
    checkDate?: string;
    amount?: number;
    currency?: string;
    reference?: string;
    status?: 'pending' | 'cleared' | 'rejected' | 'returned';
  }>;
  checkNumber?: string;
  bankId?: string;
  checkDate?: string;
  date?: string;
  postingStatus?: 'draft' | 'saved' | 'posted';
  currencyCode?: string;
  exchangeRate?: number;
  rateDate?: string;
  isAdvance?: boolean;
  reference?: string;
  notes?: string;
  allocations: Array<{
    invoiceId: string;
    amount: number;
  }>;
  userId?: string;
  userName?: string;
};

async function getBaseCurrencyCode(): Promise<string> {
  
  const pgDefaultCurrency = await pgGetDefaultCurrency();
  return String(pgDefaultCurrency?.code || 'JOD').trim() || 'JOD';
}

async function resolveExchangeRate(params: {
  paymentCurrency: string;
  baseCurrencyCode: string;
  providedExchangeRate?: number;
}): Promise<number> {
  const { paymentCurrency, baseCurrencyCode, providedExchangeRate } = params;
  if (paymentCurrency === baseCurrencyCode) return 1;
  const normalizedProvided = Number(providedExchangeRate || 0);
  if (Number.isFinite(normalizedProvided) && normalizedProvided > 0) {
    return normalizedProvided;
  }
  return getStoredExchangeRate(paymentCurrency, baseCurrencyCode);
}

async function applyReceiptAllocations(params: {
  allocations: Array<{ invoiceId: string; amount: number }>;
  method: SalesInvoicePaymentMethod['method'];
  reference?: string;
  userId: string;
  userName: string;
  paymentDateIso: string;
}) {
  const service = getSalesInvoiceService();
  for (const allocation of params.allocations) {
    await service.recordPayment(
      allocation.invoiceId,
      allocation.amount,
      {
        method: params.method,
        amount: allocation.amount,
        date: params.paymentDateIso,
        reference: params.reference,
      },
      params.userId,
      params.userName
    );
  }
}

async function reverseReceiptAllocations(params: {
  allocations: Array<{ invoiceId: string; amount: number }>;
  userId: string;
  userName: string;
  note?: string;
}) {
  const service = getSalesInvoiceService();
  for (const allocation of params.allocations) {
    await service.reversePayment(
      allocation.invoiceId,
      allocation.amount,
      params.userId,
      params.userName,
      params.note
    );
  }
}

export async function handleListSalesPaymentReceipts(params?: {
  customerId?: string;
  take?: number;
}): Promise<{ success: boolean; data?: { items: any[]; total: number }; error?: string }> {
  try {
    const take = Math.max(1, Number(params?.take || 200));
    const customerId = String(params?.customerId || '').trim();

    const rows = (await pgGetPayments({ page: 1, pageSize: Math.max(take, 500) })).items;
    const filtered = (rows || [])
      .filter((entry: any) => (customerId ? String(entry.customerId || '') === customerId : true))
      .sort((a: any, b: any) => new Date(String(b.date || b.createdAt || 0)).getTime() - new Date(String(a.date || a.createdAt || 0)).getTime())
      .slice(0, take);

    return {
      success: true,
      data: {
        items: filtered,
        total: filtered.length,
      },
    };
  } catch (error) {
    console.error('Failed to list sales payment receipts:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

export async function handleCreateSalesPaymentReceipt(
  input: SalesPaymentReceiptInput
): Promise<{ success: boolean; data?: { appliedAmount: number; unappliedAmount: number }; error?: string }> {
  try {
    const service = getSalesInvoiceService();
    const postingStatus = input.postingStatus || 'posted';
    const paymentAmount = Number(input.amount || 0);
    const customerId = String(input.customerId || '').trim();
    const userId = String(input.userId || 'unknown');
    const userName = String(input.userName || 'Unknown User');
    const baseCurrencyCode = await getBaseCurrencyCode();
    const paymentCurrency = String(input.currencyCode || baseCurrencyCode).trim() || baseCurrencyCode;
    const paymentExchangeRate = await resolveExchangeRate({
      paymentCurrency,
      baseCurrencyCode,
      providedExchangeRate: input.exchangeRate,
    });
    const paymentDateIso = input.date
      ? new Date(input.date).toISOString()
      : new Date().toISOString();
    const rateDateIso = input.rateDate
      ? new Date(input.rateDate).toISOString()
      : paymentDateIso;

    if (!customerId) {
      return { success: false, error: 'Customer is required.' };
    }
    if (!Number.isFinite(paymentAmount) || paymentAmount <= 0) {
      return { success: false, error: 'Payment amount must be greater than 0.' };
    }

    const normalizedAllocations = (input.allocations || [])
      .map((allocation) => ({
        invoiceId: String(allocation.invoiceId || '').trim(),
        amount: Number(allocation.amount || 0),
      }))
      .filter((allocation) => allocation.invoiceId && Number.isFinite(allocation.amount) && allocation.amount > 0);

    if (normalizedAllocations.length === 0) {
      return { success: false, error: 'At least one valid allocation is required.' };
    }

    const lockAttempt = await acquireSalesReceiptLocks({
      customerId,
      invoiceIds: normalizedAllocations.map((allocation) => allocation.invoiceId),
      userId,
      userName,
    });
    if (!lockAttempt.success) {
      return { success: false, error: lockAttempt.error };
    }

    try {

    const listed = await service.list({ customerId, take: 500 });
    const invoiceMap = new Map(
      (listed.items || []).map((invoice) => [invoice.id, invoice])
    );

    for (const allocation of normalizedAllocations) {
      const invoice = invoiceMap.get(allocation.invoiceId);
      if (!invoice) {
        return { success: false, error: `Invoice not found for allocation: ${allocation.invoiceId}` };
      }
      if (invoice.status === 'cancelled') {
        return { success: false, error: `Invoice is cancelled: ${invoice.invoiceNumber}` };
      }
      if (allocation.amount - Number(invoice.amountDue || 0) > 0.01) {
        return { success: false, error: `Allocation exceeds amount due for invoice ${invoice.invoiceNumber}.` };
      }
    }

    let allocatedTotalInPaymentCurrency = 0;
    for (const allocation of normalizedAllocations) {
      const invoice = invoiceMap.get(allocation.invoiceId);
      const invoiceCurrency = String(invoice?.currencyCode || baseCurrencyCode);
      const invoiceExchangeRate = Number(
        invoice?.exchangeRate || await getStoredExchangeRate(invoiceCurrency, baseCurrencyCode)
      );

      const amountInPaymentCurrency = invoiceCurrency === paymentCurrency
        ? allocation.amount
        : Number.isFinite(invoiceExchangeRate) && invoiceExchangeRate > 0
          ? (allocation.amount * invoiceExchangeRate) / paymentExchangeRate
          : allocation.amount;

      allocatedTotalInPaymentCurrency += amountInPaymentCurrency;
    }

    if (Math.abs(allocatedTotalInPaymentCurrency - paymentAmount) > 0.01) {
      return { success: false, error: 'Allocated amount must equal payment amount.' };
    }

    let paymentReference = String(input.reference || '').trim() || undefined;
    if (paymentReference) {
      const referenceValidation = await validateManualSalesReceiptReference({
        reference: paymentReference,
        ignorePaymentId: input.ignoreReferencePaymentId,
      });
      if (referenceValidation.success === false) {
        return { success: false, error: referenceValidation.error };
      }
      paymentReference = referenceValidation.normalizedReference;
    } else {
      paymentReference = await allocateSalesReceiptReference(paymentDateIso);
    }

    let totalFxDifferenceBase = 0;
    const allocationDetails: Array<{
      invoiceId: string;
      invoiceNumber: string | undefined;
      invoiceCurrencyCode: string;
      invoiceExchangeRate: number;
      invoiceRateDate: string | undefined;
      amount: number;
      amountBase: number;
      fxDifferenceBase: number;
    }> = [];

    for (const allocation of normalizedAllocations) {
      const invoice = invoiceMap.get(allocation.invoiceId);
      const invoiceCurrency = String(invoice?.currencyCode || baseCurrencyCode);
      const invoiceExchangeRate = Number(
        invoice?.exchangeRate || await getStoredExchangeRate(invoiceCurrency, baseCurrencyCode)
      );
      const amountInPaymentCurrency = invoiceCurrency === paymentCurrency
        ? allocation.amount
        : Number.isFinite(invoiceExchangeRate) && invoiceExchangeRate > 0
          ? (allocation.amount * invoiceExchangeRate) / paymentExchangeRate
          : allocation.amount;
      const amountBase = amountInPaymentCurrency * paymentExchangeRate;
      const baseAtInvoiceRate = allocation.amount * (Number.isFinite(invoiceExchangeRate) && invoiceExchangeRate > 0 ? invoiceExchangeRate : 1);
      const fxDifferenceBase = amountBase - baseAtInvoiceRate;
      totalFxDifferenceBase += fxDifferenceBase;

      allocationDetails.push({
        invoiceId: allocation.invoiceId,
        invoiceNumber: invoice?.invoiceNumber,
        invoiceCurrencyCode: invoiceCurrency,
        invoiceExchangeRate,
        invoiceRateDate: invoice?.rateDate,
        amount: allocation.amount,
        amountBase,
        fxDifferenceBase,
      });
    }

    if (postingStatus === 'posted') {
      await applyReceiptAllocations({
        allocations: normalizedAllocations,
        method: input.method,
        reference: paymentReference,
        userId,
        userName,
        paymentDateIso,
      });
    }

    // Infer salesRepId from allocated invoices
    let inferredSalesRepId = '';
    if (normalizedAllocations.length > 0) {
      const repIds = new Map<string, number>(); // rep ID -> total amount
      for (const alloc of normalizedAllocations) {
        const invoice = invoiceMap.get(alloc.invoiceId);
        if (invoice?.salesRepId) {
          const currentAmount = repIds.get(invoice.salesRepId) || 0;
          repIds.set(invoice.salesRepId, currentAmount + Number(alloc.amount || 0));
        }
      }
      
      if (repIds.size === 1) {
        // Single rep: use it directly
        inferredSalesRepId = Array.from(repIds.keys())[0];
      } else if (repIds.size > 1) {
        // Multiple reps: use the one with largest total allocation
        let maxRepId = '';
        let maxAmount = 0;
        repIds.forEach((amount, repId) => {
          if (amount > maxAmount) {
            maxAmount = amount;
            maxRepId = repId;
          }
        });
        inferredSalesRepId = maxRepId;
      }
    }
    
    // Note: If no invoice has a salesRepId, leave it empty.
    // Fallback to customer rep could be added if customer data is available to the action.

    const paymentRecord = {
      status: 'active' as const,
      date: paymentDateIso,
      customerId,
      amount: paymentAmount,
      amountBase: paymentAmount * paymentExchangeRate,
      currencyCode: paymentCurrency,
      baseCurrencyCode,
      exchangeRate: paymentExchangeRate,
      rateDate: rateDateIso,
      isAdvance: Boolean(input.isAdvance),
      method: input.method,
      postingStatus,
      reference: paymentReference,
      notes: String(input.notes || '').trim() || undefined,
      allocations: allocationDetails,
      appliedAmount: postingStatus === 'posted' ? paymentAmount : 0,
      unappliedAmount: postingStatus === 'posted' ? 0 : paymentAmount,
      totalFxDifferenceBase,
      createdBy: userName,
      salesRepId: inferredSalesRepId || undefined,
    };

    const persistedPayment = await pgAddPayment(paymentRecord);
    const customerName = String(invoiceMap.get(normalizedAllocations[0]?.invoiceId)?.customerName || customerId);
    await syncIncomingCheckForReceipt({
      receiptId: String((persistedPayment as any)?.id || ''),
      method: input.method,
      postingStatus,
      customerId,
      customerName,
      amount: paymentAmount,
      currencyCode: paymentCurrency,
      exchangeRate: paymentExchangeRate,
      receiptChecks: input.receiptChecks,
      checkNumber: input.checkNumber,
      bankId: input.bankId,
      checkDate: input.checkDate || paymentDateIso.slice(0, 10),
      reference: paymentReference,
      createdBy: userName,
      noteSuffix: `Sales receipt ${(persistedPayment as any)?.id || ''}`,
    });

    try {
      await syncRealEstateCollectionsFromSalesReceipts({
        invoiceIds: normalizedAllocations.map((allocation) => allocation.invoiceId),
        paymentDate: paymentDateIso,
      });
    } catch (realEstateSyncError) {
      console.error('Failed to sync real estate collections after receipt create:', realEstateSyncError);
    }

    return {
      success: true,
      data: {
        appliedAmount: postingStatus === 'posted' ? paymentAmount : 0,
        unappliedAmount: postingStatus === 'posted' ? 0 : paymentAmount,
      },
    };
    } finally {
      await releaseSalesReceiptLocks(lockAttempt.acquiredKeys, userId);
    }
  } catch (error) {
    console.error('Failed to create sales payment receipt:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

type SalesPaymentReceiptUpdateInput = {
  paymentId: string;
  amount: number;
  method: SalesInvoicePaymentMethod['method'];
  receiptChecks?: Array<{
    checkNumber?: string;
    bankId?: string;
    bankName?: string;
    checkDate?: string;
    amount?: number;
    currency?: string;
    reference?: string;
    status?: 'pending' | 'cleared' | 'rejected' | 'returned';
  }>;
  checkNumber?: string;
  bankId?: string;
  checkDate?: string;
  postingStatus?: 'draft' | 'saved' | 'posted';
  currencyCode?: string;
  exchangeRate?: number;
  rateDate?: string;
  isAdvance?: boolean;
  reference?: string;
  notes?: string;
  allocations: Array<{
    invoiceId: string;
    amount: number;
  }>;
  userId?: string;
  userName?: string;
};

export async function handleUpdateSalesPaymentReceipt(
  input: SalesPaymentReceiptUpdateInput
): Promise<{ success: boolean; data?: { appliedAmount: number; unappliedAmount: number }; error?: string }> {
  try {
    const paymentId = String(input.paymentId || '').trim();
    if (!paymentId) {
      return { success: false, error: 'Payment ID is required.' };
    }

    const existingRows = (await pgGetPayments({ page: 1, pageSize: 5000 })).items;    const existing = (existingRows || []).find((row: any) => String(row.id || '') === paymentId) as any;
    if (!existing) {
      return { success: false, error: 'Payment voucher not found.' };
    }
    if (existing.status === 'cancelled') {
      return { success: false, error: 'Cancelled payment voucher cannot be modified.' };
    }

    const normalizedUserId = String(input.userId || 'unknown');
    const normalizedUserName = String(input.userName || 'Unknown User');

    const oldAllocations = (existing.allocations || [])
      .map((entry: any) => ({
        invoiceId: String(entry.invoiceId || '').trim(),
        amount: Number(entry.amount || 0),
      }))
      .filter((entry: any) => entry.invoiceId && Number.isFinite(entry.amount) && entry.amount > 0);

    const lockAttempt = await acquireSalesReceiptLocks({
      customerId: String(existing.customerId || ''),
      invoiceIds: [
        ...oldAllocations.map((entry: any) => String(entry.invoiceId || '')),
        ...(input.allocations || []).map((entry) => String(entry.invoiceId || '')),
      ],
      userId: normalizedUserId,
      userName: normalizedUserName,
    });
    if (!lockAttempt.success) {
      return { success: false, error: lockAttempt.error };
    }

    try {

    const wasPosted = (existing.postingStatus || 'posted') === 'posted' && Number(existing.appliedAmount || 0) > 0;
    let reversedOldImpact = false;
    if (wasPosted) {
      if (oldAllocations.length === 0) {
        return {
          success: false,
          error: 'Cannot update posted receipt because existing allocation lines are missing.',
        };
      }
      await reverseReceiptAllocations({
        allocations: oldAllocations,
        userId: normalizedUserId,
        userName: normalizedUserName,
        note: `Receipt ${paymentId} reversed for update`,
      });
      reversedOldImpact = true;
    }

    const createResult = await handleCreateSalesPaymentReceipt({
      customerId: String(existing.customerId || ''),
      amount: input.amount,
      method: input.method,
      receiptChecks: input.receiptChecks,
      checkNumber: input.checkNumber,
      bankId: input.bankId,
      checkDate: input.checkDate,
      postingStatus: input.postingStatus || existing.postingStatus || 'saved',
      currencyCode: input.currencyCode || existing.currencyCode,
      exchangeRate: input.exchangeRate || existing.exchangeRate,
      rateDate: input.rateDate || existing.rateDate,
      isAdvance: input.isAdvance,
      reference: input.reference,
      ignoreReferencePaymentId: paymentId,
      notes: input.notes,
      allocations: input.allocations,
      userId: normalizedUserId,
      userName: normalizedUserName,
    });

    if (!createResult.success) {
      if (reversedOldImpact) {
        try {
          await applyReceiptAllocations({
            allocations: oldAllocations,
            method: (existing.method as SalesInvoicePaymentMethod['method']) || 'cash',
            reference: existing.reference,
            userId: normalizedUserId,
            userName: normalizedUserName,
            paymentDateIso: new Date().toISOString(),
          });
        } catch (rollbackError) {
          console.error('Rollback failed after receipt update failure:', rollbackError);
        }
      }
      return createResult;
    }

    const listAfterCreate = (await pgGetPayments({ page: 1, pageSize: 5000 })).items;    const latestForCustomer = (listAfterCreate || [])
      .filter((row: any) => String(row.customerId || '') === String(existing.customerId || ''))
      .sort((a: any, b: any) => new Date(String(b.date || 0)).getTime() - new Date(String(a.date || 0)).getTime())[0] as any;

    if (!latestForCustomer) {
      return { success: false, error: 'Failed to persist updated receipt.' };
    }

    const updateResult = await pgUpdatePayment(paymentId, {
          status: 'cancelled',
          cancelledAt: new Date().toISOString(),
          cancelledBy: normalizedUserName,
          cancelledReason: `Superseded by ${latestForCustomer.id}`,
        });
    if (!updateResult?.success) {
      return { success: false, error: updateResult?.error || 'Failed to archive previous receipt.' };
    }

    await markReceiptIncomingChecksAsReturned(paymentId, `Superseded by ${latestForCustomer.id}`);

    return {
      success: true,
      data: createResult.data,
    };
    } finally {
      await releaseSalesReceiptLocks(lockAttempt.acquiredKeys, normalizedUserId);
    }
  } catch (error) {
    console.error('Failed to update sales payment receipt:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

export async function handleCancelSalesPaymentReceipt(
  paymentId: string,
  reason?: string,
  userName?: string,
  userId?: string
): Promise<{ success: boolean; error?: string }> {
  try {
    const normalizedPaymentId = String(paymentId || '').trim();
    if (!normalizedPaymentId) {
      return { success: false, error: 'Payment ID is required.' };
    }

    const rows = (await pgGetPayments({ page: 1, pageSize: 5000 })).items;    const existing = (rows || []).find((entry: any) => String(entry.id || '') === normalizedPaymentId) as any;
    if (!existing) {
      return { success: false, error: 'Payment voucher not found.' };
    }
    if (existing.status === 'cancelled') {
      return { success: false, error: 'Payment voucher is already cancelled.' };
    }

    const normalizedUserId = String(userId || 'unknown');
    const normalizedUserName = String(userName || 'Unknown User');

    const oldAllocations = (existing.allocations || [])
      .map((entry: any) => ({
        invoiceId: String(entry.invoiceId || '').trim(),
        amount: Number(entry.amount || 0),
      }))
      .filter((entry: any) => entry.invoiceId && Number.isFinite(entry.amount) && entry.amount > 0);

    const lockAttempt = await acquireSalesReceiptLocks({
      customerId: String(existing.customerId || ''),
      invoiceIds: oldAllocations.map((entry: any) => String(entry.invoiceId || '')),
      userId: normalizedUserId,
      userName: normalizedUserName,
    });
    if (!lockAttempt.success) {
      return { success: false, error: lockAttempt.error };
    }

    try {

    const isPostedAndApplied = (existing.postingStatus || 'posted') === 'posted' && Number(existing.appliedAmount || 0) > 0;
    if (isPostedAndApplied) {
      if (oldAllocations.length === 0) {
        return {
          success: false,
          error: 'Cannot cancel posted receipt because allocation lines are missing.',
        };
      }

      await reverseReceiptAllocations({
        allocations: oldAllocations,
        userId: normalizedUserId,
        userName: normalizedUserName,
        note: `Receipt ${normalizedPaymentId} reversed for cancellation`,
      });
    }

    const result = await pgCancelPayment(normalizedPaymentId, normalizedUserName, reason);
    if (!result?.success) {
      return { success: false, error: result?.error || 'Failed to cancel receipt.' };
    }

    await markReceiptIncomingChecksAsReturned(normalizedPaymentId, reason || 'Sales receipt cancelled');

    try {
      await syncRealEstateCollectionsFromSalesReceipts({
        invoiceIds: oldAllocations.map((entry: any) => String(entry.invoiceId || '')).filter(Boolean),
      });
    } catch (realEstateSyncError) {
      console.error('Failed to sync real estate collections after receipt cancellation:', realEstateSyncError);
    }

    return { success: true };
    } finally {
      await releaseSalesReceiptLocks(lockAttempt.acquiredKeys, normalizedUserId);
    }
  } catch (error) {
    console.error('Failed to cancel sales payment receipt:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

/**
 * Issue debit note against an existing sales invoice
 */
export async function handleIssueSalesDebitNote(
  id: string,
  amount: number,
  reason: string,
  userId: string,
  userName: string
): Promise<{ success: boolean; data?: SalesInvoice; error?: string }> {
  const normalizedInvoiceId = String(id || '').trim();
  const normalizedUserId = String(userId || '').trim();
  const normalizedUserName = String(userName || '').trim() || 'Unknown User';
  const lockKey = `sales-debit-note:invoice:${normalizedInvoiceId}`;
  let lockAcquired = false;

  try {
    const lockAttempt = await pgAcquireSupplierPaymentLock(lockKey, normalizedUserId, normalizedUserName);
    if (!lockAttempt?.success) {
      return {
        success: false,
        error: `Cannot continue because this invoice is being processed by ${String(lockAttempt?.lockedBy || 'another user')}.`,
      };
    }
    lockAcquired = true;

    const service = getSalesInvoiceService();
    const invoice = await service.issueDebitNote(normalizedInvoiceId, {
      amount,
      reason,
      userId: normalizedUserId,
      userName: normalizedUserName,
    });

    return { success: true, data: invoice };
  } catch (error) {
    console.error('Failed to issue sales debit note:', error);
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  } finally {
    if (lockAcquired && normalizedUserId) {
      try {
        await pgReleaseSupplierPaymentLock(lockKey, normalizedUserId);
      } catch {
        // Non-blocking lock release failure.
      }
    }
  }
}
