/// Domain Entity: Quote
///
/// Represents a price quotation for products/services.
library;

/// Quote status enum
enum QuoteStatus {
  /// Quote is in draft state
  draft,

  /// Quote has been sent to customer
  sent,

  /// Customer has accepted the quote
  accepted,

  /// Quote has been rejected
  rejected,

  /// Quote has expired
  expired,

  /// Quote has been converted to order
  converted;

  /// Get display name for status
  String get displayName {
    switch (this) {
      case QuoteStatus.draft:
        return 'Draft';
      case QuoteStatus.sent:
        return 'Sent';
      case QuoteStatus.accepted:
        return 'Accepted';
      case QuoteStatus.rejected:
        return 'Rejected';
      case QuoteStatus.expired:
        return 'Expired';
      case QuoteStatus.converted:
        return 'Converted';
    }
  }
}

/// Delivery Address information
class DeliveryAddress {
  /// 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 DeliveryAddress({
    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 DeliveryAddress.fromJson(Map<String, dynamic> json) {
    return DeliveryAddress(
      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,
    };
  }
}

/// Quote Entity
///
/// Contains complete quotation information:
/// - Quote identification
/// - Customer details
/// - Pricing and terms
/// - Conversion tracking
class Quote {
  /// Unique quote identifier
  final String quoteId;

  /// Quote number (human-readable)
  final String quoteNumber;

  /// User ID who requested the quote
  final String userId;

  /// Quote status
  final QuoteStatus status;

  /// Total amount before discount
  final double totalAmount;

  /// Discount amount
  final double discountAmount;

  /// Final amount after discount
  final double finalAmount;

  /// Project name (if applicable)
  final String? projectName;

  /// Delivery address
  final DeliveryAddress? deliveryAddress;

  /// Payment terms
  final String? paymentTerms;

  /// Quote notes
  final String? notes;

  /// Valid until date
  final DateTime? validUntil;

  /// Converted order ID (if converted)
  final String? convertedOrderId;

  /// ERPNext quotation reference
  final String? erpnextQuotation;

  /// Quote creation timestamp
  final DateTime createdAt;

  /// Last update timestamp
  final DateTime updatedAt;

  const Quote({
    required this.quoteId,
    required this.quoteNumber,
    required this.userId,
    required this.status,
    required this.totalAmount,
    required this.discountAmount,
    required this.finalAmount,
    this.projectName,
    this.deliveryAddress,
    this.paymentTerms,
    this.notes,
    this.validUntil,
    this.convertedOrderId,
    this.erpnextQuotation,
    required this.createdAt,
    required this.updatedAt,
  });

  /// Check if quote is draft
  bool get isDraft => status == QuoteStatus.draft;

  /// Check if quote is sent
  bool get isSent => status == QuoteStatus.sent;

  /// Check if quote is accepted
  bool get isAccepted => status == QuoteStatus.accepted;

  /// Check if quote is rejected
  bool get isRejected => status == QuoteStatus.rejected;

  /// Check if quote is expired
  bool get isExpired =>
      status == QuoteStatus.expired ||
      (validUntil != null && DateTime.now().isAfter(validUntil!));

  /// Check if quote is converted to order
  bool get isConverted => status == QuoteStatus.converted;

  /// Check if quote can be edited
  bool get canBeEdited => isDraft;

  /// Check if quote can be sent
  bool get canBeSent => isDraft;

  /// Check if quote can be converted to order
  bool get canBeConverted => isAccepted && !isExpired;

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

  /// Get days until expiry
  int? get daysUntilExpiry {
    if (validUntil == null) return null;
    final days = validUntil!.difference(DateTime.now()).inDays;
    return days > 0 ? days : 0;
  }

  /// Check if expiring soon (within 7 days)
  bool get isExpiringSoon {
    if (validUntil == null || isExpired) return false;
    final days = daysUntilExpiry;
    return days != null && days > 0 && days <= 7;
  }

  /// Copy with method for immutability
  Quote copyWith({
    String? quoteId,
    String? quoteNumber,
    String? userId,
    QuoteStatus? status,
    double? totalAmount,
    double? discountAmount,
    double? finalAmount,
    String? projectName,
    DeliveryAddress? deliveryAddress,
    String? paymentTerms,
    String? notes,
    DateTime? validUntil,
    String? convertedOrderId,
    String? erpnextQuotation,
    DateTime? createdAt,
    DateTime? updatedAt,
  }) {
    return Quote(
      quoteId: quoteId ?? this.quoteId,
      quoteNumber: quoteNumber ?? this.quoteNumber,
      userId: userId ?? this.userId,
      status: status ?? this.status,
      totalAmount: totalAmount ?? this.totalAmount,
      discountAmount: discountAmount ?? this.discountAmount,
      finalAmount: finalAmount ?? this.finalAmount,
      projectName: projectName ?? this.projectName,
      deliveryAddress: deliveryAddress ?? this.deliveryAddress,
      paymentTerms: paymentTerms ?? this.paymentTerms,
      notes: notes ?? this.notes,
      validUntil: validUntil ?? this.validUntil,
      convertedOrderId: convertedOrderId ?? this.convertedOrderId,
      erpnextQuotation: erpnextQuotation ?? this.erpnextQuotation,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
    );
  }

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

    return other is Quote &&
        other.quoteId == quoteId &&
        other.quoteNumber == quoteNumber &&
        other.userId == userId &&
        other.status == status &&
        other.totalAmount == totalAmount &&
        other.discountAmount == discountAmount &&
        other.finalAmount == finalAmount;
  }

  @override
  int get hashCode {
    return Object.hash(
      quoteId,
      quoteNumber,
      userId,
      status,
      totalAmount,
      discountAmount,
      finalAmount,
    );
  }

  @override
  String toString() {
    return 'Quote(quoteId: $quoteId, quoteNumber: $quoteNumber, status: $status, '
        'finalAmount: $finalAmount, validUntil: $validUntil)';
  }
}
