import 'package:firebase_auth/firebase_auth.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:em2rp/config/api_config.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:em2rp/utils/debug_log.dart';

/// Interface abstraite pour les opérations API
/// Permet de changer facilement de backend (Firebase Functions, REST API personnalisé, etc.)
abstract class ApiService {
  Future<Map<String, dynamic>> call(String functionName, Map<String, dynamic> data);
  Future<T?> get<T>(String endpoint, {Map<String, dynamic>? params});
  Future<T> post<T>(String endpoint, Map<String, dynamic> data);
  Future<T> put<T>(String endpoint, Map<String, dynamic> data);
  Future<void> delete(String endpoint, {Map<String, dynamic>? data});
}

/// Implémentation pour Firebase Cloud Functions
class FirebaseFunctionsApiService implements ApiService {
  // URL de base - gérée par ApiConfig
  String get _baseUrl => ApiConfig.baseUrl;

  /// Récupère le token d'authentification Firebase
  Future<String?> _getAuthToken() async {
    final user = FirebaseAuth.instance.currentUser;
    if (user == null) return null;
    return await user.getIdToken();
  }

  /// Headers par défaut avec authentification
  Future<Map<String, String>> _getHeaders() async {
    final token = await _getAuthToken();
    return {
      'Content-Type': 'application/json',
      if (token != null) 'Authorization': 'Bearer $token',
    };
  }

  /// Convertit récursivement TOUT en types JSON standards (String, num, bool, List, Map)
  /// Garantit que toutes les Maps sont des Map<String, dynamic> littérales
  dynamic _toJsonSafe(dynamic value) {
    if (value == null) return null;

    // Types primitifs JSON-safe
    if (value is String || value is num || value is bool) {
      return value;
    }

    // Types Firestore
    if (value is Timestamp) {
      return value.toDate().toIso8601String();
    }
    if (value is DateTime) {
      return value.toIso8601String();
    }
    if (value is DocumentReference) {
      return value.path;
    }
    if (value is GeoPoint) {
      // Créer une Map littérale explicite
      return <String, dynamic>{
        'latitude': value.latitude,
        'longitude': value.longitude,
      };
    }

    // Listes - créer une nouvelle List littérale
    if (value is List) {
      final result = <dynamic>[];
      for (final item in value) {
        result.add(_toJsonSafe(item));
      }
      return result;
    }

    // Maps - créer une nouvelle Map littérale explicite
    if (value is Map) {
      final result = <String, dynamic>{};
      value.forEach((k, v) {
        final key = k.toString();
        final convertedValue = _toJsonSafe(v);
        result[key] = convertedValue;
      });
      return result;
    }

    // Type non supporté - retourner en String
    return value.toString();
  }

  /// Prépare les données pour jsonEncode en faisant un double passage
  Map<String, dynamic> _prepareForJson(Map<String, dynamic> data) {
    try {
      // Premier passage : convertir tous les types Firestore
      final safeData = _toJsonSafe(data);

      // Deuxième passage : encoder puis décoder pour forcer la normalisation
      // Cela garantit que tout est 100% compatible JSON et élimine tous les _JsonMap
      final jsonString = jsonEncode(safeData);
      final decoded = jsonDecode(jsonString);

      // Force le type Map<String, dynamic>
      if (decoded is Map) {
        return Map<String, dynamic>.from(decoded);
      }

      // Fallback - ne devrait jamais arriver
      return Map<String, dynamic>.from(safeData as Map);
    } catch (e) {
      // Si l'encodage échoue, essayer de créer une copie profonde manuelle
      DebugLog.error('[API] Error in _prepareForJson', e);
      DebugLog.info('[API] Trying manual deep copy...');
      return _deepCopyMap(data);
    }
  }

  /// Copie profonde manuelle d'une Map pour éviter les _JsonMap
  Map<String, dynamic> _deepCopyMap(Map<String, dynamic> source) {
    final result = <String, dynamic>{};
    source.forEach((key, value) {
      if (value is Map) {
        result[key] = _deepCopyMap(Map<String, dynamic>.from(value));
      } else if (value is List) {
        result[key] = _deepCopyList(value);
      } else {
        result[key] = value;
      }
    });
    return result;
  }

  /// Copie profonde manuelle d'une List
  List<dynamic> _deepCopyList(List<dynamic> source) {
    return source.map((item) {
      if (item is Map) {
        return _deepCopyMap(Map<String, dynamic>.from(item));
      } else if (item is List) {
        return _deepCopyList(item);
      } else {
        return item;
      }
    }).toList();
  }

  @override
  Future<Map<String, dynamic>> call(String functionName, Map<String, dynamic> data) async {
    final url = Uri.parse('$_baseUrl/$functionName');
    final headers = await _getHeaders();

    // Préparer les données avec double passage pour éviter les _JsonMap
    final preparedData = _prepareForJson(data);

    // Log pour débogage (seulement en mode debug)
    DebugLog.info('[API] Calling $functionName with eventId: ${preparedData['eventId']}');

    try {
      // Encoder directement avec jsonEncode standard
      final bodyJson = jsonEncode({'data': preparedData});

      final response = await http.post(
        url,
        headers: headers,
        body: bodyJson,
      );

      if (response.statusCode >= 200 && response.statusCode < 300) {
        final responseData = jsonDecode(response.body);
        return responseData is Map<String, dynamic> ? responseData : {};
      } else {
        final error = jsonDecode(response.body);
        throw ApiException(
          message: error['error'] ?? 'Unknown error',
          statusCode: response.statusCode,
        );
      }
    } on ApiException {
      rethrow;
    } catch (e) {
      DebugLog.error('[API] Error during request: $functionName', e);
      throw ApiException(
        message: 'Error calling $functionName: $e',
        statusCode: 0,
      );
    }
  }

  @override
  Future<T?> get<T>(String endpoint, {Map<String, dynamic>? params}) async {
    final url = Uri.parse('$_baseUrl/$endpoint').replace(queryParameters: params);
    final headers = await _getHeaders();

    final response = await http.get(url, headers: headers);

    if (response.statusCode >= 200 && response.statusCode < 300) {
      final responseData = jsonDecode(response.body);
      return responseData as T?;
    } else if (response.statusCode == 404) {
      return null;
    } else {
      final error = jsonDecode(response.body);
      throw ApiException(
        message: error['error'] ?? 'Unknown error',
        statusCode: response.statusCode,
      );
    }
  }

  @override
  Future<T> post<T>(String endpoint, Map<String, dynamic> data) async {
    final url = Uri.parse('$_baseUrl/$endpoint');
    final headers = await _getHeaders();

    // Préparer les données avec double passage
    final preparedData = _prepareForJson(data);

    final response = await http.post(
      url,
      headers: headers,
      body: jsonEncode({'data': preparedData}),
    );

    if (response.statusCode >= 200 && response.statusCode < 300) {
      final responseData = jsonDecode(response.body);
      return responseData as T;
    } else {
      final error = jsonDecode(response.body);
      throw ApiException(
        message: error['error'] ?? 'Unknown error',
        statusCode: response.statusCode,
      );
    }
  }

  @override
  Future<T> put<T>(String endpoint, Map<String, dynamic> data) async {
    final url = Uri.parse('$_baseUrl/$endpoint');
    final headers = await _getHeaders();

    // Préparer les données avec double passage
    final preparedData = _prepareForJson(data);

    final response = await http.put(
      url,
      headers: headers,
      body: jsonEncode({'data': preparedData}),
    );

    if (response.statusCode >= 200 && response.statusCode < 300) {
      final responseData = jsonDecode(response.body);
      return responseData as T;
    } else {
      final error = jsonDecode(response.body);
      throw ApiException(
        message: error['error'] ?? 'Unknown error',
        statusCode: response.statusCode,
      );
    }
  }

  @override
  Future<void> delete(String endpoint, {Map<String, dynamic>? data}) async {
    final url = Uri.parse('$_baseUrl/$endpoint');
    final headers = await _getHeaders();

    // Préparer les données avec double passage si data existe
    final preparedData = data != null ? _prepareForJson(data) : null;

    final response = await http.delete(
      url,
      headers: headers,
      body: preparedData != null ? jsonEncode({'data': preparedData}) : null,
    );

    if (response.statusCode < 200 || response.statusCode >= 300) {
      final error = jsonDecode(response.body);
      throw ApiException(
        message: error['error'] ?? 'Unknown error',
        statusCode: response.statusCode,
      );
    }
  }

  /// Appelle une Cloud Function avec pagination
  Future<Map<String, dynamic>> callPaginated(
    String functionName,
    Map<String, dynamic> params,
  ) async {
    try {
      final headers = await _getHeaders();
      final url = Uri.parse('$_baseUrl/$functionName');

      DebugLog.info('[API] Calling paginated function: $functionName with params: $params');

      final response = await http.post(
        url,
        headers: headers,
        body: jsonEncode({'data': params}),
      );

      DebugLog.info('[API] Response status: ${response.statusCode}');

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body) as Map<String, dynamic>;
        return data;
      } else {
        DebugLog.error('[API] Error response: ${response.body}');
        throw Exception('API call failed with status ${response.statusCode}: ${response.body}');
      }
    } catch (e) {
      DebugLog.error('[API] Exception in callPaginated: $e');
      rethrow;
    }
  }

  /// Recherche rapide avec autocomplétion
  Future<List<Map<String, dynamic>>> quickSearch(
    String query, {
    int limit = 10,
    bool includeEquipments = true,
    bool includeContainers = true,
  }) async {
    try {
      final params = {
        'query': query,
        'limit': limit,
        'includeEquipments': includeEquipments.toString(),
        'includeContainers': includeContainers.toString(),
      };

      final response = await callPaginated('quickSearch', params);
      final results = response['results'] as List<dynamic>? ?? [];

      return results.cast<Map<String, dynamic>>();
    } catch (e) {
      DebugLog.error('[API] Error in quickSearch: $e');
      return [];
    }
  }
}

/// Exception personnalisée pour les erreurs API
class ApiException implements Exception {
  final String message;
  final int statusCode;

  ApiException({
    required this.message,
    required this.statusCode,
  });

  @override
  String toString() => 'ApiException($statusCode): $message';

  bool get isForbidden => statusCode == 403;
  bool get isUnauthorized => statusCode == 401;
  bool get isNotFound => statusCode == 404;
  bool get isConflict => statusCode == 409;
}

/// Instance singleton du service API
final ApiService apiService = FirebaseFunctionsApiService();

