/// Modèle de données pour les contributions
library contribution_model;

import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';

part 'contribution_model.g.dart';

/// Statut d'une contribution
enum ContributionStatus {
  @JsonValue('PAYEE')
  payee,
  @JsonValue('NON_PAYEE')
  nonPayee,
  @JsonValue('EN_ATTENTE')
  enAttente,
  @JsonValue('EN_RETARD')
  enRetard,
  @JsonValue('PARTIELLE')
  partielle,
  @JsonValue('ANNULEE')
  annulee,
}

/// Type de contribution
enum ContributionType {
  @JsonValue('ANNUELLE')
  annuelle,
  @JsonValue('MENSUELLE')
  mensuelle,
  @JsonValue('TRIMESTRIELLE')
  trimestrielle,
  @JsonValue('SEMESTRIELLE')
  semestrielle,
  @JsonValue('EXCEPTIONNELLE')
  exceptionnelle,
}

/// Méthode de paiement
enum PaymentMethod {
  @JsonValue('ESPECES')
  especes,
  @JsonValue('CHEQUE')
  cheque,
  @JsonValue('VIREMENT')
  virement,
  @JsonValue('CARTE_BANCAIRE')
  carteBancaire,
  @JsonValue('WAVE_MONEY')
  waveMoney,
  @JsonValue('ORANGE_MONEY')
  orangeMoney,
  @JsonValue('FREE_MONEY')
  freeMoney,
  @JsonValue('MOBILE_MONEY')
  mobileMoney,
  @JsonValue('AUTRE')
  autre,
}

/// Extension pour obtenir le code API d'une méthode de paiement (ex: pour icônes assets).
extension PaymentMethodCode on PaymentMethod {
  String get code {
    switch (this) {
      case PaymentMethod.especes: return 'ESPECES';
      case PaymentMethod.cheque: return 'CHEQUE';
      case PaymentMethod.virement: return 'VIREMENT';
      case PaymentMethod.carteBancaire: return 'CARTE_BANCAIRE';
      case PaymentMethod.waveMoney: return 'WAVE_MONEY';
      case PaymentMethod.orangeMoney: return 'ORANGE_MONEY';
      case PaymentMethod.freeMoney: return 'FREE_MONEY';
      case PaymentMethod.mobileMoney: return 'MOBILE_MONEY';
      case PaymentMethod.autre: return 'AUTRE';
    }
  }
}

/// Modèle complet d'une contribution
@JsonSerializable(explicitToJson: true)
class ContributionModel extends Equatable {
  /// Identifiant unique
  final String? id;

  /// Membre concerné
  final String membreId;
  final String? membreNom;
  final String? membrePrenom;

  /// Organisation
  final String? organisationId;
  final String? organisationNom;

  /// Informations de la contribution
  final ContributionType type;
  final ContributionStatus statut;
  final double montant;
  final double? montantPaye;
  final String devise;

  /// Dates
  final DateTime dateEcheance;
  final DateTime? datePaiement;
  final DateTime? dateRappel;

  /// Paiement
  final PaymentMethod? methodePaiement;
  final String? numeroPaiement;
  final String? referencePaiement;

  /// Période
  final int annee;
  final int? mois;
  final int? trimestre;
  final int? semestre;

  /// Informations complémentaires
  final String? description;
  final String? notes;
  final String? recu;

  /// Métadonnées
  final DateTime? dateCreation;
  final DateTime? dateModification;
  final String? creeParId;
  final String? modifieParId;

  const ContributionModel({
    this.id,
    required this.membreId,
    this.membreNom,
    this.membrePrenom,
    this.organisationId,
    this.organisationNom,
    this.type = ContributionType.annuelle,
    this.statut = ContributionStatus.nonPayee,
    required this.montant,
    this.montantPaye,
    this.devise = 'XOF',
    required this.dateEcheance,
    this.datePaiement,
    this.dateRappel,
    this.methodePaiement,
    this.numeroPaiement,
    this.referencePaiement,
    required this.annee,
    this.mois,
    this.trimestre,
    this.semestre,
    this.description,
    this.notes,
    this.recu,
    this.dateCreation,
    this.dateModification,
    this.creeParId,
    this.modifieParId,
  });

  /// Désérialisation depuis JSON
  factory ContributionModel.fromJson(Map<String, dynamic> json) =>
      _$ContributionModelFromJson(json);

  /// Sérialisation vers JSON
  Map<String, dynamic> toJson() => _$ContributionModelToJson(this);

  /// Copie avec modifications
  ContributionModel copyWith({
    String? id,
    String? membreId,
    String? membreNom,
    String? membrePrenom,
    String? organisationId,
    String? organisationNom,
    ContributionType? type,
    ContributionStatus? statut,
    double? montant,
    double? montantPaye,
    String? devise,
    DateTime? dateEcheance,
    DateTime? datePaiement,
    DateTime? dateRappel,
    PaymentMethod? methodePaiement,
    String? numeroPaiement,
    String? referencePaiement,
    int? annee,
    int? mois,
    int? trimestre,
    int? semestre,
    String? description,
    String? notes,
    String? recu,
    DateTime? dateCreation,
    DateTime? dateModification,
    String? creeParId,
    String? modifieParId,
  }) {
    return ContributionModel(
      id: id ?? this.id,
      membreId: membreId ?? this.membreId,
      membreNom: membreNom ?? this.membreNom,
      membrePrenom: membrePrenom ?? this.membrePrenom,
      organisationId: organisationId ?? this.organisationId,
      organisationNom: organisationNom ?? this.organisationNom,
      type: type ?? this.type,
      statut: statut ?? this.statut,
      montant: montant ?? this.montant,
      montantPaye: montantPaye ?? this.montantPaye,
      devise: devise ?? this.devise,
      dateEcheance: dateEcheance ?? this.dateEcheance,
      datePaiement: datePaiement ?? this.datePaiement,
      dateRappel: dateRappel ?? this.dateRappel,
      methodePaiement: methodePaiement ?? this.methodePaiement,
      numeroPaiement: numeroPaiement ?? this.numeroPaiement,
      referencePaiement: referencePaiement ?? this.referencePaiement,
      annee: annee ?? this.annee,
      mois: mois ?? this.mois,
      trimestre: trimestre ?? this.trimestre,
      semestre: semestre ?? this.semestre,
      description: description ?? this.description,
      notes: notes ?? this.notes,
      recu: recu ?? this.recu,
      dateCreation: dateCreation ?? this.dateCreation,
      dateModification: dateModification ?? this.dateModification,
      creeParId: creeParId ?? this.creeParId,
      modifieParId: modifieParId ?? this.modifieParId,
    );
  }

  /// Nom complet du membre
  String get membreNomComplet {
    if (membreNom != null && membrePrenom != null) {
      return '$membrePrenom $membreNom';
    }
    return membreNom ?? membrePrenom ?? 'Membre inconnu';
  }

  /// Montant restant à payer
  double get montantRestant {
    if (montantPaye == null) return montant;
    return montant - montantPaye!;
  }

  /// Pourcentage payé
  double get pourcentagePaye {
    if (montantPaye == null || montant == 0) return 0;
    return (montantPaye! / montant) * 100;
  }

  /// Vérifie si la contribution est payée
  bool get estPayee => statut == ContributionStatus.payee;

  /// Vérifie si la contribution est en retard
  bool get estEnRetard {
    if (estPayee) return false;
    return DateTime.now().isAfter(dateEcheance);
  }

  /// Nombre de jours avant/après l'échéance
  int get joursAvantEcheance {
    return dateEcheance.difference(DateTime.now()).inDays;
  }

  /// Libellé de la période
  String get libellePeriode {
    switch (type) {
      case ContributionType.annuelle:
        return 'Année $annee';
      case ContributionType.mensuelle:
        if (mois != null) {
          return '${_getNomMois(mois!)} $annee';
        }
        return 'Année $annee';
      case ContributionType.trimestrielle:
        if (trimestre != null) {
          return 'T$trimestre $annee';
        }
        return 'Année $annee';
      case ContributionType.semestrielle:
        if (semestre != null) {
          return 'S$semestre $annee';
        }
        return 'Année $annee';
      case ContributionType.exceptionnelle:
        return 'Exceptionnelle $annee';
    }
  }

  /// Nom du mois
  String _getNomMois(int mois) {
    const moisFr = [
      'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
      'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'
    ];
    if (mois >= 1 && mois <= 12) {
      return moisFr[mois - 1];
    }
    return 'Mois $mois';
  }

  @override
  List<Object?> get props => [
        id,
        membreId,
        membreNom,
        membrePrenom,
        organisationId,
        organisationNom,
        type,
        statut,
        montant,
        montantPaye,
        devise,
        dateEcheance,
        datePaiement,
        dateRappel,
        methodePaiement,
        numeroPaiement,
        referencePaiement,
        annee,
        mois,
        trimestre,
        semestre,
        description,
        notes,
        recu,
        dateCreation,
        dateModification,
        creeParId,
        modifieParId,
      ];

  @override
  String toString() =>
      'ContributionModel(id: $id, membre: $membreNomComplet, montant: $montant $devise, statut: $statut)';
}

