import 'dart:convert';
import 'dart:typed_data';

import '../models/user_registration_data.dart';
import 'email_utils.dart';

/// Construction du body `POST /auth/register/parent` à partir du state d'inscription.
/// Aligné sur [RegisterParentCompletDto] (backend).
class ParentRegistrationPayload {
  ParentRegistrationPayload._();

  static final RegExp _phoneFr = RegExp(r'^(\+33|0)[1-9](\d{2}){4}$');
  /// Aligné sur `GenreType` (backend) : JSON exact `H`, `F`, `Autre`.
  static const Set<String> apiGenres = {'H', 'F', 'Autre'};

  /// Retourne un message d'erreur utilisateur, ou `null` si le formulaire est cohérent.
  static String? validateForApi(UserRegistrationData d) {
    final p1 = d.parent1;
    final p1Email = normalizeEmailText(p1.email);
    if (p1Email.isEmpty) {
      return 'L’email du parent principal est requis.';
    }
    if (!isValidEmailFormat(p1Email)) {
      return 'L’email du parent principal n’est pas au bon format.';
    }
    if (p1.firstName.trim().length < 2) {
      return 'Le prénom du parent principal doit contenir au moins 2 caractères.';
    }
    if (p1.lastName.trim().length < 2) {
      return 'Le nom du parent principal doit contenir au moins 2 caractères.';
    }
    final tel = _normalizePhone(p1.phone);
    if (!_phoneFr.hasMatch(tel)) {
      return 'Le numéro de téléphone du parent principal n’est pas valide (ex. 0612345678).';
    }
    if (!d.cguAccepted) {
      return 'Vous devez accepter les CGU et la politique de confidentialité.';
    }
    if (d.children.isEmpty) {
      return 'Au moins un enfant est requis.';
    }

    final p2 = d.parent2;
    if (p2 != null) {
      final any = p2.email.trim().isNotEmpty ||
          p2.firstName.trim().isNotEmpty ||
          p2.lastName.trim().isNotEmpty ||
          p2.phone.trim().isNotEmpty;
      if (any) {
        final p2Email = normalizeEmailText(p2.email);
        if (p2Email.isEmpty ||
            p2.firstName.trim().length < 2 ||
            p2.lastName.trim().length < 2) {
          return 'Les informations du co-parent sont incomplètes (email, prénom et nom requis).';
        }
        if (!isValidEmailFormat(p2Email)) {
          return 'L’email du co-parent n’est pas au bon format.';
        }
        if (p2Email == p1Email) {
          return 'L’email du co-parent doit être différent de celui du parent principal.';
        }
        final tel2 = _normalizePhone(p2.phone);
        if (tel2.isNotEmpty && !_phoneFr.hasMatch(tel2)) {
          return 'Le numéro de téléphone du co-parent n’est pas valide.';
        }
      }
    }

    for (var i = 0; i < d.children.length; i++) {
      final c = d.children[i];
      final label = 'Enfant ${i + 1}';
      if (!c.photoConsent) {
        return '$label : le consentement photo est obligatoire.';
      }
      if (c.isUnbornChild) {
        final due = _ddMmYyyyToIso(c.dob);
        if (due == null) {
          return '$label : indiquez une date de naissance prévisionnelle.';
        }
        if (!apiGenres.contains(c.genre)) {
          return '$label : indiquez Fille, Garçon ou Inconnu.';
        }
      } else {
        if (c.firstName.trim().length < 2) {
          return '$label : le prénom doit contenir au moins 2 caractères.';
        }
        final birth = _ddMmYyyyToIso(c.dob);
        if (birth == null) {
          return '$label : indiquez une date de naissance valide.';
        }
        if (c.genre != 'H' && c.genre != 'F') {
          return '$label : indiquez Fille ou Garçon.';
        }
      }
    }

    return null;
  }

  static Map<String, dynamic> toJson(UserRegistrationData d) {
    final p1 = d.parent1;
    final tel = _normalizePhone(p1.phone);

    final body = <String, dynamic>{
      'email': normalizeEmailText(p1.email),
      'prenom': p1.firstName.trim(),
      'nom': p1.lastName.trim(),
      'telephone': tel,
      'acceptation_cgu': d.cguAccepted,
      'acceptation_privacy': d.cguAccepted,
    };

    _putIfNonEmpty(body, 'adresse', p1.address.trim());
    _putIfNonEmpty(body, 'code_postal', p1.postalCode.trim());
    _putIfNonEmpty(body, 'ville', p1.city.trim());

    final p2 = d.parent2;
    if (p2 != null &&
        p2.email.trim().isNotEmpty &&
        p2.firstName.trim().length >= 2 &&
        p2.lastName.trim().length >= 2) {
      final tel2 = _normalizePhone(p2.phone);
      final sameAddr = p2.address.trim() == p1.address.trim() &&
          p2.postalCode.trim() == p1.postalCode.trim() &&
          p2.city.trim() == p1.city.trim();

      body['co_parent_email'] = normalizeEmailText(p2.email);
      body['co_parent_prenom'] = p2.firstName.trim();
      body['co_parent_nom'] = p2.lastName.trim();
      body['co_parent_meme_adresse'] = sameAddr;
      if (tel2.isNotEmpty) {
        body['co_parent_telephone'] = tel2;
      }
      if (!sameAddr) {
        _putIfNonEmpty(body, 'co_parent_adresse', p2.address.trim());
        _putIfNonEmpty(body, 'co_parent_code_postal', p2.postalCode.trim());
        _putIfNonEmpty(body, 'co_parent_ville', p2.city.trim());
      }
    }

    if (d.motivationText.trim().isNotEmpty) {
      body['presentation_dossier'] = d.motivationText.trim();
    }

    body['enfants'] = d.children.asMap().entries.map((e) {
      return _childToJson(e.value, e.key, d.parent1.lastName.trim());
    }).toList();

    return body;
  }

  /// Enfant pour PATCH reprise (inclut `id` si connu).
  static Map<String, dynamic> childToRepriseJson(
    ChildData c,
    int index,
    String parentNom,
  ) {
    final map = _childToJson(c, index, parentNom);
    final id = c.repriseChildId?.trim();
    if (id != null && id.isNotEmpty) {
      map['id'] = id;
    }
    return map;
  }

  static Map<String, dynamic> _childToJson(ChildData c, int index, String parentNom) {
    final map = <String, dynamic>{
      'genre': apiGenres.contains(c.genre) ? c.genre : 'Autre',
      'grossesse_multiple': c.multipleBirth,
      'consent_photo': c.photoConsent,
    };

    final prenom = c.firstName.trim();
    if (prenom.length >= 2) {
      map['prenom'] = prenom;
    }

    final nom = c.lastName.trim();
    if (nom.length >= 2) {
      map['nom'] = nom;
    } else if (parentNom.length >= 2) {
      map['nom'] = parentNom;
    }

    if (c.isUnbornChild) {
      final due = _ddMmYyyyToIso(c.dob);
      if (due != null) {
        map['date_previsionnelle_naissance'] = due;
      }
    } else {
      final birth = _ddMmYyyyToIso(c.dob);
      if (birth != null) {
        map['date_naissance'] = birth;
      }
    }

    final photo = _childPhotoBase64(c, index, prenom);
    if (photo != null) {
      map['photo_base64'] = photo.$1;
      map['photo_filename'] = photo.$2;
    }

    return map;
  }

  /// Sous-type `image/…` pour data-URL et extension côté API (`jpeg`, `png`, `gif`, `heic`, …).
  static String _imageMimeFromMagicBytes(Uint8List bytes) {
    if (bytes.length >= 8) {
      if (bytes[0] == 0x89 &&
          bytes[1] == 0x50 &&
          bytes[2] == 0x4E &&
          bytes[3] == 0x47) {
        return 'png';
      }
      if (bytes[0] == 0xFF && bytes[1] == 0xD8) {
        return 'jpeg';
      }
      if (bytes.length >= 12 &&
          bytes[0] == 0x52 &&
          bytes[1] == 0x49 &&
          bytes[2] == 0x46 &&
          bytes[3] == 0x46) {
        return 'webp';
      }
      if (bytes.length >= 6 &&
          bytes[0] == 0x47 &&
          bytes[1] == 0x49 &&
          bytes[2] == 0x46 &&
          bytes[3] == 0x38 &&
          (bytes[4] == 0x37 || bytes[4] == 0x39) &&
          bytes[5] == 0x61) {
        return 'gif';
      }
      if (bytes.length >= 12 &&
          bytes[4] == 0x66 &&
          bytes[5] == 0x74 &&
          bytes[6] == 0x79 &&
          bytes[7] == 0x70) {
        final a = bytes[8], b = bytes[9], c = bytes[10], d = bytes[11];
        if ((a == 0x68 && b == 0x65 && c == 0x69 && d == 0x63) ||
            (a == 0x68 && b == 0x65 && c == 0x69 && d == 0x78) ||
            (a == 0x6d && b == 0x69 && c == 0x66 && d == 0x31) ||
            (a == 0x6d && b == 0x73 && c == 0x66 && d == 0x31)) {
          return 'heic';
        }
      }
    }
    return 'jpeg';
  }

  /// (`dataUrl`, `filename`) ou `null` si pas de fichier lisible.
  static (String, String)? _childPhotoBase64(ChildData c, int index, String prenom) {
    Uint8List? bytes = c.imageBytes;
    if (bytes == null || bytes.isEmpty) {
      final file = c.imageFile;
      if (file == null) return null;
      try {
        if (!file.existsSync()) return null;
        bytes = file.readAsBytesSync();
      } catch (_) {
        return null;
      }
    }
    if (bytes.isEmpty) return null;
    final b64 = base64Encode(bytes);
    final mime = _imageMimeFromMagicBytes(bytes);
    final safeName = prenom.isNotEmpty
        ? '${prenom.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), '_')}.$mime'
        : 'enfant_${index + 1}.$mime';
    return ('data:image/$mime;base64,$b64', safeName);
  }

  static void _putIfNonEmpty(Map<String, dynamic> m, String key, String value) {
    if (value.isNotEmpty) {
      m[key] = value;
    }
  }

  static String _normalizePhone(String raw) {
    var s = raw.replaceAll(RegExp(r'\s'), '');
    if (s.startsWith('0033')) {
      s = '+33${s.substring(4)}';
    }
    return s;
  }

  /// `jj/mm/aaaa` → `aaaa-mm-jj`, ou `null` si invalide.
  static String? _ddMmYyyyToIso(String ddMmYyyy) {
    if (ddMmYyyy.isEmpty) return null;
    final parts = ddMmYyyy.split('/');
    if (parts.length != 3) return null;
    final day = int.tryParse(parts[0]);
    final month = int.tryParse(parts[1]);
    final year = int.tryParse(parts[2]);
    if (day == null || month == null || year == null) return null;
    if (month < 1 || month > 12 || day < 1 || day > 31) return null;
    return '${year.toString().padLeft(4, '0')}-${month.toString().padLeft(2, '0')}-${day.toString().padLeft(2, '0')}';
  }
}
