import '../../domain/entities/pokemon.dart';

/// Conversion JSON <-> entité Pokemon. Unique endroit de parsing.
class PokemonDto {
  PokemonDto._();

  static PokemonType _frenchTypeToEnum(String type) {
    const map = {
      'Normal': PokemonType.normal,
      'Combat': PokemonType.fighting,
      'Vol': PokemonType.flying,
      'Poison': PokemonType.poison,
      'Sol': PokemonType.ground,
      'Roche': PokemonType.rock,
      'Insecte': PokemonType.bug,
      'Spectre': PokemonType.ghost,
      'Acier': PokemonType.steel,
      'Feu': PokemonType.fire,
      'Eau': PokemonType.water,
      'Plante': PokemonType.grass,
      'Électrik': PokemonType.electric,
      'Psy': PokemonType.psychic,
      'Glace': PokemonType.ice,
      'Dragon': PokemonType.dragon,
      'Ténèbres': PokemonType.dark,
      'Fée': PokemonType.fairy,
    };
    return map[type] ?? PokemonType.unknown;
  }

  // Tyradex always returns type names in French regardless of endpoint language.
  static PokemonType _typeToEnum(String typeName, String languageCode) {
    return _frenchTypeToEnum(typeName);
  }

  /// Construit une entité depuis la réponse Tyradex.
  /// [languageCode] détermine quelle langue utiliser pour [name] ('fr' ou 'en').
  static Pokemon fromTyradexJson(
    Map<String, dynamic> json, {
    int? fallbackId,
    String languageCode = 'fr',
  }) {
    final id = (json['pokedex_id'] as int?) ??
        fallbackId ??
        (throw ArgumentError('pokedex_id absent et fallbackId non fourni'));

    final nameMap = json['name'] as Map<String, dynamic>?;
    final nameFr = nameMap?['fr'] as String? ?? 'unknown';
    final nameEn = nameMap?['en'] as String? ?? nameFr;
    final name = languageCode == 'en' ? nameEn : nameFr;

    final List types = json['types'] ?? [];
    // Tyradex retourne toujours les types dans la langue du endpoint (fr ou en).
    final type1 = types.isNotEmpty
        ? _typeToEnum(types[0]['name'] as String, languageCode)
        : PokemonType.unknown;
    final type2 = types.length > 1
        ? _typeToEnum(types[1]['name'] as String, languageCode)
        : null;

    final Map<String, dynamic>? stats = json['stats'];
    // Tyradex expose 'category' (FR) et parfois 'category_en' (EN).
    final descFr = json['category'] as String?;
    final descEn = (json['category_en'] ?? json['category']) as String?;
    return Pokemon(
      name: name,
      nameFr: nameFr,
      nameEn: nameEn,
      id: id,
      type1: type1,
      type2: type2,
      hp: stats?['hp'] ?? 0,
      atk: stats?['atk'] ?? 0,
      def: stats?['def'] ?? 0,
      spd: stats?['vit'] ?? 0,
      description: descFr,
      descriptionEn: descEn,
    );
  }

  /// Sérialise pour SQLite (stocke les deux noms et descriptions).
  static Map<String, dynamic> toDb(Pokemon p) {
    return {
      'name': p.name,
      'name_fr': p.nameFr ?? p.name,
      'name_en': p.nameEn ?? p.name,
      'id': p.id,
      'type1': p.type1.name,
      'type2': p.type2?.name,
      'hp': p.hp,
      'atk': p.atk,
      'def': p.def,
      'spd': p.spd,
      'description': p.description,
      'description_en': p.descriptionEn,
      'isCaught': p.isCaught ? 1 : 0,
      'isSeen': p.isSeen ? 1 : 0,
    };
  }

  /// Reconstruit depuis une ligne SQLite.
  /// [languageCode] détermine quel nom afficher.
  static Pokemon fromDb(Map<String, dynamic> row, {String languageCode = 'fr'}) {
    final nameFr = row['name_fr'] as String? ?? row['name'] as String;
    final nameEn = row['name_en'] as String? ?? nameFr;
    final name = languageCode == 'en' ? nameEn : nameFr;

    return Pokemon(
      name: name,
      nameFr: nameFr,
      nameEn: nameEn,
      id: row['id'],
      type1: PokemonType.values.firstWhere(
        (e) => e.name == row['type1'],
        orElse: () => PokemonType.unknown,
      ),
      type2: row['type2'] != null
          ? PokemonType.values.firstWhere(
              (e) => e.name == row['type2'],
              orElse: () => PokemonType.unknown,
            )
          : null,
      hp: row['hp'] ?? 0,
      atk: row['atk'] ?? 0,
      def: row['def'] ?? 0,
      spd: row['spd'] ?? 0,
      description: row['description'],
      descriptionEn: row['description_en'] as String?,
      isCaught: row['isCaught'] == 1 || row['isCaught'] == true,
      isSeen: row['isSeen'] == 1 || row['isSeen'] == true,
    );
  }
}
