/**
 * Sales Invoice Domain Service
 * Pure business logic calculations and validations
 * No dependencies on infrastructure or external services
 */

import {
  SalesInvoice,
  SalesInvoiceItem,
  CreateSalesInvoiceRecord,
  TaxMethod,
} from './sales-invoice.model';

/**
 * Calculate totals for a sales invoice
 */
export function calculateSalesInvoiceTotals(params: {
  items: Array<{
    quantity: number;
    unitPrice: number;
    lineDiscountPercent?: number;
    lineDiscountAmount?: number;
    taxRate?: number;
  }>;
  taxMethod: TaxMethod;
  invoiceDiscountPercent?: number;
  invoiceDiscountAmount?: number;
}): {
  subTotal: number;
  lineDiscountTotal: number;
  totalBeforeTax: number;
  taxAmount: number;
  grandTotal: number;
} {
  // Calculate line totals and discounts
  let subTotal = 0;
  let lineDiscountTotal = 0;

  for (const item of params.items) {
    const lineTotal = item.quantity * item.unitPrice;
    subTotal += lineTotal;

    // Calculate line discount
    if (item.lineDiscountPercent && item.lineDiscountPercent > 0) {
      lineDiscountTotal += lineTotal * (item.lineDiscountPercent / 100);
    }
    if (item.lineDiscountAmount && item.lineDiscountAmount > 0) {
      lineDiscountTotal += item.lineDiscountAmount;
    }
  }

  const subtotalAfterLineDiscount = subTotal - lineDiscountTotal;

  // Calculate invoice-level discount
  let invoiceDiscount = 0;
  if (params.invoiceDiscountPercent && params.invoiceDiscountPercent > 0) {
    invoiceDiscount =
      subtotalAfterLineDiscount *
      (params.invoiceDiscountPercent / 100);
  }
  if (params.invoiceDiscountAmount && params.invoiceDiscountAmount > 0) {
    invoiceDiscount += params.invoiceDiscountAmount;
  }

  const totalBeforeTax = subtotalAfterLineDiscount - invoiceDiscount;

  // Calculate tax based on tax method
  let taxAmount: number;
  if (params.taxMethod === 'tax-inclusive') {
    // Tax is already included in prices - no additional tax
    taxAmount = 0;
  } else if (params.taxMethod === 'invoice-level') {
    // Calculate tax on total before tax (not implemented for now, 0% for safety)
    taxAmount = 0;
  } else if (params.taxMethod === 'line-level') {
    // Tax calculated per line
    let lineTaxTotal = 0;
    for (const item of params.items) {
      const lineTotal = item.quantity * item.unitPrice;
      const lineAfterDiscount = item.lineDiscountPercent
        ? lineTotal * (1 - item.lineDiscountPercent / 100)
        : item.lineDiscountAmount
          ? lineTotal - item.lineDiscountAmount
          : lineTotal;

      if (item.taxRate && item.taxRate > 0) {
        lineTaxTotal += lineAfterDiscount * (item.taxRate / 100);
      }
    }
    taxAmount = lineTaxTotal;
  } else {
    // tax-exempt or unknown
    taxAmount = 0;
  }

  const grandTotal = totalBeforeTax + taxAmount;

  return {
    subTotal,
    lineDiscountTotal,
    totalBeforeTax,
    taxAmount,
    grandTotal,
  };
}

/**
 * Validate a sales invoice for creation
 */
export function validateSalesInvoice(record: CreateSalesInvoiceRecord): {
  valid: boolean;
  errors: string[];
} {
  const errors: string[] = [];

  // Validate customer
  if (!record.customerId || record.customerId.trim() === '') {
    errors.push('Customer ID is required');
  }

  const primaryCustomerId = String(record.customerId || '').trim();
  const secondaryCustomerId = String(record.secondaryCustomerId || '').trim();
  const secondaryCoveragePercent = Number(record.secondaryCoveragePercent || 0);

  if (secondaryCustomerId && secondaryCustomerId === primaryCustomerId) {
    errors.push('Secondary customer must be different from primary customer');
  }

  if (secondaryCustomerId) {
    if (!Number.isFinite(secondaryCoveragePercent) || secondaryCoveragePercent <= 0 || secondaryCoveragePercent >= 100) {
      errors.push('Secondary coverage percent must be greater than 0 and less than 100 when secondary customer exists');
    }
  } else if (secondaryCoveragePercent > 0) {
    errors.push('Secondary customer is required when secondary coverage percent is greater than 0');
  }

  // Validate date
  if (!record.date) {
    errors.push('Invoice date is required');
  } else {
    const dateObj = new Date(record.date);
    if (isNaN(dateObj.getTime())) {
      errors.push('Invalid invoice date format');
    }
  }

  // Validate items
  if (!record.items || record.items.length === 0) {
    errors.push('At least one item is required');
  } else {
    for (let i = 0; i < record.items.length; i++) {
      const item = record.items[i];
      const hasMaterial = Boolean(String(item.materialId || '').trim());
      const hasRealEstateUnit = Boolean(String(item.realEstateProjectId || '').trim() && String(item.realEstateUnitId || '').trim());

      if (!hasMaterial && !hasRealEstateUnit) {
        errors.push(`Item ${i + 1}: Material ID or real-estate unit is required`);
      }
      if (item.quantity <= 0) {
        errors.push(`Item ${i + 1}: Quantity must be greater than 0`);
      }
      if (item.unitPrice < 0) {
        errors.push(`Item ${i + 1}: Unit price cannot be negative`);
      }
    }
  }

  // Validate amounts
  if (record.amountReceived < 0) {
    errors.push('Amount received cannot be negative');
  }

  if (record.currencyCode && Array.isArray(record.paymentMethods)) {
    for (const method of record.paymentMethods) {
      if (method.method === 'check' && method.currency && method.currency !== record.currencyCode) {
        errors.push('Check payment currency must match the invoice currency');
        break;
      }
    }
  }

  if (record.taxMethod === 'tax-exempt') {
    if (!String(record.taxExemptionReason || '').trim()) {
      errors.push('Tax exemption reason is required for tax-exempt invoices');
    }
    if (!String(record.taxExemptionReference || '').trim()) {
      errors.push('Tax exemption reference is required for tax-exempt invoices');
    }
  }

  const hasRealEstateContext = Boolean(String(record.realEstateProjectId || '').trim() && String(record.realEstateUnitId || '').trim());
  if (hasRealEstateContext) {
    if (Number(record.realEstateContractValue || 0) <= 0) {
      errors.push('Real-estate contract value must be greater than 0');
    }
    if (record.realEstateInstallmentMode === 'auto') {
      if (Number(record.realEstateInstallmentCount || 0) <= 0) {
        errors.push('Installment count is required for auto-generated real-estate schedules');
      }
      if (!String(record.realEstateFirstDueDate || '').trim()) {
        errors.push('First due date is required for auto-generated real-estate schedules');
      }
    }
  }

  return {
    valid: errors.length === 0,
    errors,
  };
}

/**
 * Calculate payment status based on amounts
 */
export function calculatePaymentStatus(
  grandTotal: number,
  amountReceived: number,
  daysOverdue: number = 0
): 'due' | 'partial' | 'paid' | 'overdue' {
  if (amountReceived >= grandTotal) {
    return 'paid';
  }
  if (amountReceived > 0) {
    return 'partial';
  }
  if (daysOverdue > 0) {
    return 'overdue';
  }
  return 'due';
}

/**
 * Calculate amount due
 */
export function calculateAmountDue(
  grandTotal: number,
  amountReceived: number
): number {
  const due = grandTotal - amountReceived;
  return Math.max(0, due);
}

/**
 * Validate discount percentage
 */
export function validateDiscountPercentage(
  percent: number
): { valid: boolean; error?: string } {
  if (percent < 0) {
    return { valid: false, error: 'Discount percentage cannot be negative' };
  }
  if (percent > 100) {
    return { valid: false, error: 'Discount percentage cannot exceed 100%' };
  }
  return { valid: true };
}

/**
 * Check if invoice can be cancelled
 */
export function canCancelInvoice(
  invoice: SalesInvoice,
  linkedDocumentsCount: number = 0
): { canCancel: boolean; reason?: string } {
  // Cannot cancel if already cancelled
  if (invoice.status === 'cancelled') {
    return { canCancel: false, reason: 'Invoice is already cancelled' };
  }

  // Cannot cancel if already delivered
  if (invoice.shippingStatus === 'delivered') {
    return { canCancel: false, reason: 'Cannot cancel delivered invoices' };
  }

  // Cannot cancel if has linked documents (returns, credit notes, etc)
  if (linkedDocumentsCount > 0) {
    return {
      canCancel: false,
      reason: 'Cannot cancel invoices with linked documents (returns, debit notes, etc)',
    };
  }

  return { canCancel: true };
}

/**
 * Check if invoice can be edited
 */
export function canEditInvoice(invoice: SalesInvoice): {
  canEdit: boolean;
  reason?: string;
} {
  // Cannot edit if already posted
  if (invoice.postingStatus === 'posted') {
    return { canEdit: false, reason: 'Cannot edit posted invoices' };
  }

  // Cannot edit if already shipped
  if (invoice.shippingStatus === 'shipped' || invoice.shippingStatus === 'delivered') {
    return {
      canEdit: false,
      reason: 'Cannot edit shipped invoices',
    };
  }

  // Cannot edit if locked by another user
  if (invoice.lockedById) {
    return {
      canEdit: false,
      reason: `Invoice is locked by ${invoice.lockedByName || 'another user'}`,
    };
  }

  return { canEdit: true };
}
