/// Résumé affichable pour un parent (liste pending-families).
class PendingParentLine {
  final String? email;
  final String? telephone;
  final String? codePostal;
  final String? ville;

  const PendingParentLine({
    this.email,
    this.telephone,
    this.codePostal,
    this.ville,
  });

  bool get isEmpty {
    final e = email?.trim();
    final t = telephone?.trim();
    final loc = _locationTrimmed;
    return (e == null || e.isEmpty) &&
        (t == null || t.isEmpty) &&
        (loc == null || loc.isEmpty);
  }

  String? get _locationTrimmed {
    final cp = codePostal?.trim();
    final v = ville?.trim();
    final loc = [if (cp != null && cp.isNotEmpty) cp, if (v != null && v.isNotEmpty) v]
        .join(' ')
        .trim();
    return loc.isEmpty ? null : loc;
  }
}

/// Famille en attente de validation (GET /parents/pending-families). Ticket #107.
///
/// Contrat API : `libelle`, `parentIds`, `numero_dossier`, `date_soumission`,
/// `nombre_enfants`, `emails`, éventuellement `parents` / tableaux parallèles.
class PendingFamily {
  final String libelle;
  final List<String> parentIds;
  final String? numeroDossier;

  /// Date affichée : `date_soumission` (ISO), sinon alias `cree_le` / etc.
  final DateTime? dateSoumission;

  /// Emails seuls (API) — le sous-titre utilise de préférence [parentLines].
  final List<String> emails;

  /// Une entrée par parent : email, tél., CP ville (si fournis par l’API).
  final List<PendingParentLine> parentLines;

  final int nombreEnfants;

  /// Compat : premier email.
  final String? email;

  PendingFamily({
    required this.libelle,
    required this.parentIds,
    this.numeroDossier,
    this.dateSoumission,
    this.emails = const [],
    this.parentLines = const [],
    this.nombreEnfants = 0,
    this.email,
  });

  static DateTime? _parseDate(dynamic v) {
    if (v == null) return null;
    if (v is DateTime) return v;
    if (v is String) {
      return DateTime.tryParse(v);
    }
    return null;
  }

  static List<String> _parseStringList(dynamic raw) {
    if (raw is! List) return [];
    return raw
        .map((e) => e?.toString().trim() ?? '')
        .where((s) => s.isNotEmpty)
        .toList();
  }

  static List<PendingParentLine> _parseParentLinesFromMaps(dynamic raw) {
    if (raw is! List) return [];
    final out = <PendingParentLine>[];
    for (final e in raw) {
      if (e is! Map) continue;
      final m = Map<String, dynamic>.from(e);
      final em = m['email']?.toString().trim();
      final tel = m['telephone']?.toString().trim();
      final cp = (m['code_postal'] ?? m['codePostal'])?.toString().trim();
      final ville = m['ville']?.toString().trim();
      out.add(PendingParentLine(
        email: em != null && em.isNotEmpty ? em : null,
        telephone: tel != null && tel.isNotEmpty ? tel : null,
        codePostal: cp != null && cp.isNotEmpty ? cp : null,
        ville: ville != null && ville.isNotEmpty ? ville : null,
      ));
    }
    return out;
  }

  /// Construit [parentLines] : objets `parents`, tableaux parallèles, ou emails + champs racine.
  static List<PendingParentLine> _buildParentLines(
    Map<String, dynamic> json,
    List<String> emails,
  ) {
    final fromMaps = _parseParentLinesFromMaps(
      json['parents'] ?? json['resume_parents'] ?? json['parent_summaries'] ?? json['parent_lines'],
    );
    if (fromMaps.isNotEmpty) {
      return fromMaps;
    }

    List<String>? parallel(dynamic keySingular, dynamic keyPlural) {
      final pl = json[keyPlural];
      if (pl is List) return _parseStringList(pl);
      final s = json[keySingular];
      if (s is String && s.trim().isNotEmpty) return [s.trim()];
      return null;
    }

    final tels = parallel('telephone', 'telephones');
    final cps = parallel('code_postal', 'code_postaux') ?? parallel('codePostal', 'codes_postaux');
    final villes = parallel('ville', 'villes');

    if (emails.isNotEmpty &&
        ((tels?.isNotEmpty ?? false) ||
            (cps?.isNotEmpty ?? false) ||
            (villes?.isNotEmpty ?? false))) {
      return List.generate(emails.length, (i) {
        return PendingParentLine(
          email: emails[i],
          telephone: tels != null && i < tels.length ? tels[i] : null,
          codePostal: cps != null && i < cps.length ? cps[i] : null,
          ville: villes != null && i < villes.length ? villes[i] : null,
        );
      });
    }

    final rootTel = json['telephone']?.toString().trim();
    final rootTelOk = rootTel != null && rootTel.isNotEmpty ? rootTel : null;
    final rootCp = (json['code_postal'] ?? json['codePostal'])?.toString().trim();
    final rootCpOk = rootCp != null && rootCp.isNotEmpty ? rootCp : null;
    final rootVille = json['ville']?.toString().trim();
    final rootVilleOk = rootVille != null && rootVille.isNotEmpty ? rootVille : null;

    if (emails.isNotEmpty) {
      return List.generate(emails.length, (i) {
        return PendingParentLine(
          email: emails[i],
          telephone: i == 0 ? rootTelOk : null,
          codePostal: i == 0 ? rootCpOk : null,
          ville: i == 0 ? rootVilleOk : null,
        );
      });
    }

    if (rootTelOk != null || rootCpOk != null || rootVilleOk != null) {
      final em = json['email']?.toString().trim();
      return [
        PendingParentLine(
          email: em != null && em.isNotEmpty ? em : null,
          telephone: rootTelOk,
          codePostal: rootCpOk,
          ville: rootVilleOk,
        ),
      ];
    }

    return [];
  }

  factory PendingFamily.fromJson(Map<String, dynamic> json) {
    final parentIdsRaw = json['parentIds'] ?? json['parent_ids'];
    final List<String> ids = parentIdsRaw is List
        ? (parentIdsRaw).map((e) => e?.toString() ?? '').where((s) => s.isNotEmpty).toList()
        : [];
    final libelle = json['libelle'];
    final libelleStr = libelle is String ? libelle : (libelle?.toString() ?? 'Famille');
    final nd = json['numero_dossier'] ?? json['numeroDossier'];
    final numeroDossier = (nd is String && nd.isNotEmpty) ? nd : null;

    DateTime? dateSoumission = _parseDate(
      json['date_soumission'] ?? json['dateSoumission'],
    );
    dateSoumission ??= _parseDate(
      json['cree_le'] ?? json['creeLe'] ?? json['date_inscription'],
    );

    List<String> emails = _parseStringList(json['emails']);
    if (emails.isEmpty) {
      final emailRaw = json['email'];
      if (emailRaw is String && emailRaw.trim().isNotEmpty) {
        emails = [emailRaw.trim()];
      }
    }
    final String? emailCompat = emails.isNotEmpty
        ? emails.first
        : (json['email'] is String && (json['email'] as String).trim().isNotEmpty
            ? (json['email'] as String).trim()
            : null);

    final nbRaw = json['nombre_enfants'] ?? json['nombreEnfants'];
    int nombreEnfants = 0;
    if (nbRaw is int) {
      nombreEnfants = nbRaw;
    } else if (nbRaw is num) {
      nombreEnfants = nbRaw.toInt();
    } else if (nbRaw != null) {
      nombreEnfants = int.tryParse(nbRaw.toString()) ?? 0;
    }

    var parentLines = _buildParentLines(json, emails);
    if (parentLines.isEmpty && emails.isNotEmpty) {
      parentLines = emails.map((e) => PendingParentLine(email: e)).toList();
    }

    return PendingFamily(
      libelle: libelleStr.isEmpty ? 'Famille' : libelleStr,
      parentIds: ids,
      numeroDossier: numeroDossier,
      dateSoumission: dateSoumission,
      emails: emails,
      parentLines: parentLines,
      nombreEnfants: nombreEnfants,
      email: emailCompat,
    );
  }
}
