/// Domain Entity: Invoice
///
/// Represents an invoice for an order.
library;

/// Invoice type enum
enum InvoiceType {
  /// Standard invoice
  standard,

  /// Proforma invoice
  proforma,

  /// Credit note
  creditNote,

  /// Debit note
  debitNote,
}

/// Invoice status enum
enum InvoiceStatus {
  /// Draft invoice
  draft,

  /// Invoice has been submitted
  submitted,

  /// Partially paid
  partiallyPaid,

  /// Fully paid
  paid,

  /// Overdue invoice
  overdue,

  /// Cancelled invoice
  cancelled;

  /// Get display name for status
  String get displayName {
    switch (this) {
      case InvoiceStatus.draft:
        return 'Draft';
      case InvoiceStatus.submitted:
        return 'Submitted';
      case InvoiceStatus.partiallyPaid:
        return 'Partially Paid';
      case InvoiceStatus.paid:
        return 'Paid';
      case InvoiceStatus.overdue:
        return 'Overdue';
      case InvoiceStatus.cancelled:
        return 'Cancelled';
    }
  }
}

/// Invoice Entity
///
/// Contains complete invoice information:
/// - Invoice identification
/// - Associated order
/// - Amounts and calculations
/// - Payment tracking
/// - Status and dates
class Invoice {
  /// Unique invoice identifier
  final String invoiceId;

  /// Invoice number (human-readable)
  final String invoiceNumber;

  /// User ID
  final String userId;

  /// Order ID
  final String? orderId;

  /// Invoice type
  final InvoiceType invoiceType;

  /// Issue date
  final DateTime issueDate;

  /// Due date
  final DateTime dueDate;

  /// Currency code (e.g., VND, USD)
  final String currency;

  /// Subtotal amount
  final double subtotalAmount;

  /// Tax amount
  final double taxAmount;

  /// Discount amount
  final double discountAmount;

  /// Shipping amount
  final double shippingAmount;

  /// Total amount
  final double totalAmount;

  /// Amount paid so far
  final double amountPaid;

  /// Amount remaining to be paid
  final double amountRemaining;

  /// Invoice status
  final InvoiceStatus status;

  /// Payment terms
  final String? paymentTerms;

  /// Invoice notes
  final String? notes;

  /// ERPNext invoice reference
  final String? erpnextInvoice;

  /// Creation timestamp
  final DateTime createdAt;

  /// Last update timestamp
  final DateTime updatedAt;

  /// Last reminder sent timestamp
  final DateTime? lastReminderSent;

  const Invoice({
    required this.invoiceId,
    required this.invoiceNumber,
    required this.userId,
    this.orderId,
    required this.invoiceType,
    required this.issueDate,
    required this.dueDate,
    required this.currency,
    required this.subtotalAmount,
    required this.taxAmount,
    required this.discountAmount,
    required this.shippingAmount,
    required this.totalAmount,
    required this.amountPaid,
    required this.amountRemaining,
    required this.status,
    this.paymentTerms,
    this.notes,
    this.erpnextInvoice,
    required this.createdAt,
    required this.updatedAt,
    this.lastReminderSent,
  });

  /// Check if invoice is fully paid
  bool get isPaid => status == InvoiceStatus.paid || amountRemaining <= 0;

  /// Check if invoice is overdue
  bool get isOverdue =>
      status == InvoiceStatus.overdue ||
      (!isPaid && DateTime.now().isAfter(dueDate));

  /// Check if invoice is partially paid
  bool get isPartiallyPaid => amountPaid > 0 && amountPaid < totalAmount;

  /// Get payment percentage
  double get paymentPercentage {
    if (totalAmount == 0) return 0;
    return (amountPaid / totalAmount) * 100;
  }

  /// Get days until due
  int get daysUntilDue => dueDate.difference(DateTime.now()).inDays;

  /// Get days overdue
  int get daysOverdue {
    if (!isOverdue) return 0;
    return DateTime.now().difference(dueDate).inDays;
  }

  /// Copy with method for immutability
  Invoice copyWith({
    String? invoiceId,
    String? invoiceNumber,
    String? userId,
    String? orderId,
    InvoiceType? invoiceType,
    DateTime? issueDate,
    DateTime? dueDate,
    String? currency,
    double? subtotalAmount,
    double? taxAmount,
    double? discountAmount,
    double? shippingAmount,
    double? totalAmount,
    double? amountPaid,
    double? amountRemaining,
    InvoiceStatus? status,
    String? paymentTerms,
    String? notes,
    String? erpnextInvoice,
    DateTime? createdAt,
    DateTime? updatedAt,
    DateTime? lastReminderSent,
  }) {
    return Invoice(
      invoiceId: invoiceId ?? this.invoiceId,
      invoiceNumber: invoiceNumber ?? this.invoiceNumber,
      userId: userId ?? this.userId,
      orderId: orderId ?? this.orderId,
      invoiceType: invoiceType ?? this.invoiceType,
      issueDate: issueDate ?? this.issueDate,
      dueDate: dueDate ?? this.dueDate,
      currency: currency ?? this.currency,
      subtotalAmount: subtotalAmount ?? this.subtotalAmount,
      taxAmount: taxAmount ?? this.taxAmount,
      discountAmount: discountAmount ?? this.discountAmount,
      shippingAmount: shippingAmount ?? this.shippingAmount,
      totalAmount: totalAmount ?? this.totalAmount,
      amountPaid: amountPaid ?? this.amountPaid,
      amountRemaining: amountRemaining ?? this.amountRemaining,
      status: status ?? this.status,
      paymentTerms: paymentTerms ?? this.paymentTerms,
      notes: notes ?? this.notes,
      erpnextInvoice: erpnextInvoice ?? this.erpnextInvoice,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
      lastReminderSent: lastReminderSent ?? this.lastReminderSent,
    );
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;

    return other is Invoice &&
        other.invoiceId == invoiceId &&
        other.invoiceNumber == invoiceNumber &&
        other.userId == userId &&
        other.orderId == orderId &&
        other.invoiceType == invoiceType &&
        other.totalAmount == totalAmount &&
        other.amountPaid == amountPaid &&
        other.status == status;
  }

  @override
  int get hashCode {
    return Object.hash(
      invoiceId,
      invoiceNumber,
      userId,
      orderId,
      invoiceType,
      totalAmount,
      amountPaid,
      status,
    );
  }

  @override
  String toString() {
    return 'Invoice(invoiceId: $invoiceId, invoiceNumber: $invoiceNumber, '
        'status: $status, totalAmount: $totalAmount, amountPaid: $amountPaid, '
        'amountRemaining: $amountRemaining)';
  }
}
