/// Domain Entity: Order
///
/// Represents a customer order.
library;

/// Order status enum
enum OrderStatus {
  /// Order has been created but not confirmed
  draft,

  /// Order has been confirmed
  confirmed,

  /// Order is being processed
  processing,

  /// Order is ready for shipping
  ready,

  /// Order has been shipped
  shipped,

  /// Order has been delivered
  delivered,

  /// Order has been completed
  completed,

  /// Order has been cancelled
  cancelled,

  /// Order has been returned
  returned;

  /// Get display name for status
  String get displayName {
    switch (this) {
      case OrderStatus.draft:
        return 'Draft';
      case OrderStatus.confirmed:
        return 'Confirmed';
      case OrderStatus.processing:
        return 'Processing';
      case OrderStatus.ready:
        return 'Ready';
      case OrderStatus.shipped:
        return 'Shipped';
      case OrderStatus.delivered:
        return 'Delivered';
      case OrderStatus.completed:
        return 'Completed';
      case OrderStatus.cancelled:
        return 'Cancelled';
      case OrderStatus.returned:
        return 'Returned';
    }
  }
}

/// Address information
class Address {
  /// Recipient name
  final String? name;

  /// Phone number
  final String? phone;

  /// Street address
  final String? street;

  /// Ward/commune
  final String? ward;

  /// District
  final String? district;

  /// City/province
  final String? city;

  /// Postal code
  final String? postalCode;

  const Address({
    this.name,
    this.phone,
    this.street,
    this.ward,
    this.district,
    this.city,
    this.postalCode,
  });

  /// Get full address string
  String get fullAddress {
    final parts = [
      street,
      ward,
      district,
      city,
      postalCode,
    ].where((part) => part != null && part.isNotEmpty).toList();

    return parts.join(', ');
  }

  /// Create from JSON map
  factory Address.fromJson(Map<String, dynamic> json) {
    return Address(
      name: json['name'] as String?,
      phone: json['phone'] as String?,
      street: json['street'] as String?,
      ward: json['ward'] as String?,
      district: json['district'] as String?,
      city: json['city'] as String?,
      postalCode: json['postal_code'] as String?,
    );
  }

  /// Convert to JSON map
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'phone': phone,
      'street': street,
      'ward': ward,
      'district': district,
      'city': city,
      'postal_code': postalCode,
    };
  }
}

/// Order Entity
///
/// Contains complete order information:
/// - Order identification
/// - Customer details
/// - Pricing and discounts
/// - Shipping information
/// - Status tracking
class Order {
  /// Unique order identifier
  final String orderId;

  /// Human-readable order number
  final String orderNumber;

  /// User ID who placed the order
  final String userId;

  /// Current order status
  final OrderStatus status;

  /// Total order amount before discounts
  final double totalAmount;

  /// Discount amount applied
  final double discountAmount;

  /// Tax amount
  final double taxAmount;

  /// Shipping fee
  final double shippingFee;

  /// Final amount to pay
  final double finalAmount;

  /// Shipping address
  final Address? shippingAddress;

  /// Billing address
  final Address? billingAddress;

  /// Expected delivery date
  final DateTime? expectedDeliveryDate;

  /// Actual delivery date
  final DateTime? actualDeliveryDate;

  /// Order notes
  final String? notes;

  /// Cancellation reason
  final String? cancellationReason;

  /// ERPNext sales order reference
  final String? erpnextSalesOrder;

  /// Order creation timestamp
  final DateTime createdAt;

  /// Last update timestamp
  final DateTime updatedAt;

  const Order({
    required this.orderId,
    required this.orderNumber,
    required this.userId,
    required this.status,
    required this.totalAmount,
    required this.discountAmount,
    required this.taxAmount,
    required this.shippingFee,
    required this.finalAmount,
    this.shippingAddress,
    this.billingAddress,
    this.expectedDeliveryDate,
    this.actualDeliveryDate,
    this.notes,
    this.cancellationReason,
    this.erpnextSalesOrder,
    required this.createdAt,
    required this.updatedAt,
  });

  /// Check if order is active (not cancelled or completed)
  bool get isActive =>
      status != OrderStatus.cancelled &&
      status != OrderStatus.completed &&
      status != OrderStatus.returned;

  /// Check if order can be cancelled
  bool get canBeCancelled =>
      status == OrderStatus.draft ||
      status == OrderStatus.confirmed ||
      status == OrderStatus.processing;

  /// Check if order is delivered
  bool get isDelivered =>
      status == OrderStatus.delivered || status == OrderStatus.completed;

  /// Check if order is cancelled
  bool get isCancelled => status == OrderStatus.cancelled;

  /// Get discount percentage
  double get discountPercentage {
    if (totalAmount == 0) return 0;
    return (discountAmount / totalAmount) * 100;
  }

  /// Copy with method for immutability
  Order copyWith({
    String? orderId,
    String? orderNumber,
    String? userId,
    OrderStatus? status,
    double? totalAmount,
    double? discountAmount,
    double? taxAmount,
    double? shippingFee,
    double? finalAmount,
    Address? shippingAddress,
    Address? billingAddress,
    DateTime? expectedDeliveryDate,
    DateTime? actualDeliveryDate,
    String? notes,
    String? cancellationReason,
    String? erpnextSalesOrder,
    DateTime? createdAt,
    DateTime? updatedAt,
  }) {
    return Order(
      orderId: orderId ?? this.orderId,
      orderNumber: orderNumber ?? this.orderNumber,
      userId: userId ?? this.userId,
      status: status ?? this.status,
      totalAmount: totalAmount ?? this.totalAmount,
      discountAmount: discountAmount ?? this.discountAmount,
      taxAmount: taxAmount ?? this.taxAmount,
      shippingFee: shippingFee ?? this.shippingFee,
      finalAmount: finalAmount ?? this.finalAmount,
      shippingAddress: shippingAddress ?? this.shippingAddress,
      billingAddress: billingAddress ?? this.billingAddress,
      expectedDeliveryDate: expectedDeliveryDate ?? this.expectedDeliveryDate,
      actualDeliveryDate: actualDeliveryDate ?? this.actualDeliveryDate,
      notes: notes ?? this.notes,
      cancellationReason: cancellationReason ?? this.cancellationReason,
      erpnextSalesOrder: erpnextSalesOrder ?? this.erpnextSalesOrder,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
    );
  }

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

    return other is Order &&
        other.orderId == orderId &&
        other.orderNumber == orderNumber &&
        other.userId == userId &&
        other.status == status &&
        other.totalAmount == totalAmount &&
        other.discountAmount == discountAmount &&
        other.taxAmount == taxAmount &&
        other.shippingFee == shippingFee &&
        other.finalAmount == finalAmount;
  }

  @override
  int get hashCode {
    return Object.hash(
      orderId,
      orderNumber,
      userId,
      status,
      totalAmount,
      discountAmount,
      taxAmount,
      shippingFee,
      finalAmount,
    );
  }

  @override
  String toString() {
    return 'Order(orderId: $orderId, orderNumber: $orderNumber, status: $status, '
        'finalAmount: $finalAmount, createdAt: $createdAt)';
  }
}
