import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'expense_split.dart';

/// Enumeration of supported currencies for expenses.
///
/// Each currency includes both a display symbol and standard currency code.
enum ExpenseCurrency {
  /// Euro currency
  eur('€', 'EUR'),

  /// US Dollar currency
  usd('\$', 'USD'),

  /// British Pound currency
  gbp('£', 'GBP');

  const ExpenseCurrency(this.symbol, this.code);

  /// Currency symbol for display (e.g., €, $, £)
  final String symbol;

  /// Standard currency code (e.g., EUR, USD, GBP)
  final String code;
}

/// Enumeration of expense categories with display names and icons.
///
/// Provides predefined categories for organizing travel expenses.
enum ExpenseCategory {
  /// Restaurant and food expenses
  restaurant('Restaurant', Icons.restaurant),

  /// Transportation expenses
  transport('Transport', Icons.directions_car),

  /// Accommodation and lodging expenses
  accommodation('Accommodation', Icons.hotel),

  /// Entertainment and activity expenses
  entertainment('Entertainment', Icons.local_activity),

  /// Shopping expenses
  shopping('Shopping', Icons.shopping_bag),

  /// Other miscellaneous expenses
  other('Other', Icons.category),

  /// Reimbursement for settling debts
  reimbursement('Remboursement', Icons.monetization_on);

  const ExpenseCategory(this.displayName, this.icon);

  /// Human-readable display name for the category
  final String displayName;

  /// Icon representing the category
  final IconData icon;
}

/// Model representing a travel expense.
///
/// This class encapsulates all information about an expense including
/// amount, currency, category, who paid, how it's split among participants,
/// and receipt information. It extends [Equatable] for value comparison.
class Expense extends Equatable {
  /// Unique identifier for the expense
  final String id;

  /// ID of the group this expense belongs to
  final String groupId;

  /// Description of the expense
  final String description;

  /// Amount of the expense in the original currency
  final double amount;

  /// Currency of the expense
  final ExpenseCurrency currency;

  /// Amount converted to EUR for standardized calculations
  final double amountInEur;

  /// Category of the expense
  final ExpenseCategory category;

  /// ID of the user who paid for this expense
  final String paidById;

  /// Name of the user who paid for this expense
  final String paidByName;

  /// Date when the expense occurred
  final DateTime date;

  /// Timestamp when the expense was created
  final DateTime createdAt;

  /// Timestamp when the expense was last edited (null if never edited)
  final DateTime? editedAt;

  /// Whether this expense has been edited after creation
  final bool isEdited;

  /// Whether this expense has been archived
  final bool isArchived;

  /// URL to the receipt image (optional)
  final String? receiptUrl;

  /// List of expense splits showing how the cost is divided
  final List<ExpenseSplit> splits;

  /// Creates a new [Expense] instance.
  ///
  /// All parameters except [editedAt] and [receiptUrl] are required.
  const Expense({
    required this.id,
    required this.groupId,
    required this.description,
    required this.amount,
    required this.currency,
    required this.amountInEur,
    required this.category,
    required this.paidById,
    required this.paidByName,
    required this.date,
    required this.createdAt,
    this.editedAt,
    this.isEdited = false,
    this.isArchived = false,
    this.receiptUrl,
    required this.splits,
  });

  factory Expense.fromMap(Map<String, dynamic> map, String id) {
    return Expense(
      id: id,
      groupId: map['groupId'] ?? '',
      description: map['description'] ?? '',
      amount: (map['amount'] as num?)?.toDouble() ?? 0.0,
      currency: ExpenseCurrency.values.firstWhere(
        (c) => c.code == map['currency'],
        orElse: () => ExpenseCurrency.eur,
      ),
      amountInEur: (map['amountInEur'] as num?)?.toDouble() ?? 0.0,
      category: ExpenseCategory.values.firstWhere(
        (c) => c.name == map['category'],
        orElse: () => ExpenseCategory.other,
      ),
      paidById: map['paidById'] ?? '',
      paidByName: map['paidByName'] ?? '',
      date: _parseDateTime(map['date']),
      createdAt: _parseDateTime(map['createdAt']),
      editedAt: map['editedAt'] != null
          ? _parseDateTime(map['editedAt'])
          : null,
      isEdited: map['isEdited'] ?? false,
      isArchived: map['isArchived'] ?? false,
      receiptUrl: map['receiptUrl'],
      splits:
          (map['splits'] as List?)
              ?.map((s) => ExpenseSplit.fromMap(s))
              .toList() ??
          [],
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'groupId': groupId,
      'description': description,
      'amount': amount,
      'currency': currency.code,
      'amountInEur': amountInEur,
      'category': category.name,
      'paidById': paidById,
      'paidByName': paidByName,
      'date': Timestamp.fromDate(date),
      'createdAt': Timestamp.fromDate(createdAt),
      'editedAt': editedAt != null ? Timestamp.fromDate(editedAt!) : null,
      'isEdited': isEdited,
      'isArchived': isArchived,
      'receiptUrl': receiptUrl,
      'splits': splits.map((s) => s.toMap()).toList(),
    };
  }

  static DateTime _parseDateTime(dynamic value) {
    if (value is Timestamp) return value.toDate();
    if (value is String) return DateTime.parse(value);
    if (value is DateTime) return value;
    return DateTime.now();
  }

  Expense copyWith({
    String? id,
    String? groupId,
    String? description,
    double? amount,
    ExpenseCurrency? currency,
    double? amountInEur,
    ExpenseCategory? category,
    String? paidById,
    String? paidByName,
    DateTime? date,
    DateTime? createdAt,
    DateTime? editedAt,
    bool? isEdited,
    bool? isArchived,
    String? receiptUrl,
    List<ExpenseSplit>? splits,
  }) {
    return Expense(
      id: id ?? this.id,
      groupId: groupId ?? this.groupId,
      description: description ?? this.description,
      amount: amount ?? this.amount,
      currency: currency ?? this.currency,
      amountInEur: amountInEur ?? this.amountInEur,
      category: category ?? this.category,
      paidById: paidById ?? this.paidById,
      paidByName: paidByName ?? this.paidByName,
      date: date ?? this.date,
      createdAt: createdAt ?? this.createdAt,
      editedAt: editedAt ?? this.editedAt,
      isEdited: isEdited ?? this.isEdited,
      isArchived: isArchived ?? this.isArchived,
      receiptUrl: receiptUrl ?? this.receiptUrl,
      splits: splits ?? this.splits,
    );
  }

  Expense copyWithEdit({
    String? description,
    double? amount,
    ExpenseCurrency? currency,
    double? amountInEur,
    ExpenseCategory? category,
    List<ExpenseSplit>? splits,
    String? receiptUrl,
  }) {
    return copyWith(
      description: description,
      amount: amount,
      currency: currency,
      amountInEur: amountInEur,
      category: category,
      splits: splits,
      receiptUrl: receiptUrl,
      editedAt: DateTime.now(),
      isEdited: true,
    );
  }

  // Marquer comme archivé
  Expense copyWithArchived() {
    return copyWith(isArchived: true);
  }

  // Ajouter/mettre à jour l'URL du reçu
  Expense copyWithReceipt(String receiptUrl) {
    return copyWith(receiptUrl: receiptUrl);
  }

  // Mettre à jour les splits
  Expense copyWithSplits(List<ExpenseSplit> newSplits) {
    return copyWith(splits: newSplits);
  }

  @override
  List<Object?> get props => [
    id,
    groupId,
    description,
    amount,
    currency,
    amountInEur,
    category,
    paidById,
    paidByName,
    date,
    createdAt,
    editedAt,
    isEdited,
    isArchived,
    receiptUrl,
    splits,
  ];
}
