/// Domain Entity: Loyalty Point Entry
///
/// Represents a single loyalty points transaction.
library;

/// Entry type enum
enum EntryType {
  /// Points earned
  earn,

  /// Points spent/redeemed
  redeem,

  /// Points adjusted by admin
  adjustment,

  /// Points expired
  expiry,
}

/// Entry source enum
enum EntrySource {
  /// Points from order purchase
  order,

  /// Points from referral
  referral,

  /// Points from gift redemption
  redemption,

  /// Points from project submission
  project,

  /// Points from points record
  pointsRecord,

  /// Manual adjustment by admin
  manual,

  /// Birthday bonus
  birthday,

  /// Welcome bonus
  welcome,

  /// Other source
  other,
}

/// Complaint status enum
enum ComplaintStatus {
  /// No complaint
  none,

  /// Complaint submitted
  submitted,

  /// Complaint under review
  reviewing,

  /// Complaint approved
  approved,

  /// Complaint rejected
  rejected,
}

/// Loyalty Point Entry Entity
///
/// Contains information about a single points transaction:
/// - Points amount (positive for earn, negative for redeem)
/// - Transaction type and source
/// - Reference to related entity
/// - Complaint handling
class LoyaltyPointEntry {
  /// Unique entry identifier
  final String entryId;

  /// User ID
  final String userId;

  /// Points amount (positive for earn, negative for redeem)
  final int points;

  /// Entry type
  final EntryType entryType;

  /// Source of the points
  final EntrySource source;

  /// Description of the transaction
  final String? description;

  /// Reference ID to related entity (order ID, gift ID, etc.)
  final String? referenceId;

  /// Reference type (order, gift, project, etc.)
  final String? referenceType;

  /// Complaint details (if any)
  final Map<String, dynamic>? complaint;

  /// Complaint status
  final ComplaintStatus complaintStatus;

  /// Balance after this transaction
  final int balanceAfter;

  /// Points expiry date
  final DateTime? expiryDate;

  /// Transaction timestamp
  final DateTime timestamp;

  /// ERPNext entry ID
  final String? erpnextEntryId;

  const LoyaltyPointEntry({
    required this.entryId,
    required this.userId,
    required this.points,
    required this.entryType,
    required this.source,
    this.description,
    this.referenceId,
    this.referenceType,
    this.complaint,
    required this.complaintStatus,
    required this.balanceAfter,
    this.expiryDate,
    required this.timestamp,
    this.erpnextEntryId,
  });

  /// Check if points are earned (positive)
  bool get isEarn => points > 0 && entryType == EntryType.earn;

  /// Check if points are spent (negative)
  bool get isRedeem => points < 0 && entryType == EntryType.redeem;

  /// Check if entry has complaint
  bool get hasComplaint => complaintStatus != ComplaintStatus.none;

  /// Check if complaint is pending
  bool get isComplaintPending =>
      complaintStatus == ComplaintStatus.submitted ||
      complaintStatus == ComplaintStatus.reviewing;

  /// Check if points are expired
  bool get isExpired {
    if (expiryDate == null) return false;
    return DateTime.now().isAfter(expiryDate!);
  }

  /// Check if points are expiring soon (within 30 days)
  bool get isExpiringSoon {
    if (expiryDate == null) return false;
    final daysUntilExpiry = expiryDate!.difference(DateTime.now()).inDays;
    return daysUntilExpiry > 0 && daysUntilExpiry <= 30;
  }

  /// Get absolute points value
  int get absolutePoints => points.abs();

  /// Copy with method for immutability
  LoyaltyPointEntry copyWith({
    String? entryId,
    String? userId,
    int? points,
    EntryType? entryType,
    EntrySource? source,
    String? description,
    String? referenceId,
    String? referenceType,
    Map<String, dynamic>? complaint,
    ComplaintStatus? complaintStatus,
    int? balanceAfter,
    DateTime? expiryDate,
    DateTime? timestamp,
    String? erpnextEntryId,
  }) {
    return LoyaltyPointEntry(
      entryId: entryId ?? this.entryId,
      userId: userId ?? this.userId,
      points: points ?? this.points,
      entryType: entryType ?? this.entryType,
      source: source ?? this.source,
      description: description ?? this.description,
      referenceId: referenceId ?? this.referenceId,
      referenceType: referenceType ?? this.referenceType,
      complaint: complaint ?? this.complaint,
      complaintStatus: complaintStatus ?? this.complaintStatus,
      balanceAfter: balanceAfter ?? this.balanceAfter,
      expiryDate: expiryDate ?? this.expiryDate,
      timestamp: timestamp ?? this.timestamp,
      erpnextEntryId: erpnextEntryId ?? this.erpnextEntryId,
    );
  }

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

    return other is LoyaltyPointEntry &&
        other.entryId == entryId &&
        other.userId == userId &&
        other.points == points &&
        other.entryType == entryType &&
        other.source == source &&
        other.balanceAfter == balanceAfter;
  }

  @override
  int get hashCode {
    return Object.hash(
      entryId,
      userId,
      points,
      entryType,
      source,
      balanceAfter,
    );
  }

  @override
  String toString() {
    return 'LoyaltyPointEntry(entryId: $entryId, userId: $userId, points: $points, '
        'entryType: $entryType, source: $source, balanceAfter: $balanceAfter, '
        'timestamp: $timestamp)';
  }
}
