import 'dart:convert';
import 'dart:io';

import 'package:http/http.dart' as http;

import '../../core/constants/env_config.dart';
import '../../core/constants/urls.dart';
import '../../core/errors/exceptions.dart';
import '../../core/utils/app_logger.dart';
import '../models/comment_model.dart';
import '../models/social_post_model.dart';

/// Source de données distante pour les posts sociaux.
///
/// Cette classe gère toutes les opérations liées aux posts sociaux
/// via l'API backend. Elle inclut la gestion d'erreurs, les timeouts,
/// et la validation des réponses.
///
/// **Usage:**
/// ```dart
/// final dataSource = SocialRemoteDataSource(http.Client());
/// final posts = await dataSource.getPosts();
/// ```
class SocialRemoteDataSource {
  /// Crée une nouvelle instance de [SocialRemoteDataSource].
  ///
  /// [client] Le client HTTP à utiliser pour les requêtes
  SocialRemoteDataSource(this.client);

  /// Client HTTP pour effectuer les requêtes réseau
  final http.Client client;

  /// Headers par défaut pour les requêtes
  static const Map<String, String> _defaultHeaders = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  /// Timeout pour les requêtes réseau
  Duration get _timeout => Duration(seconds: EnvConfig.networkTimeout);

  // ============================================================================
  // MÉTHODES PRIVÉES UTILITAIRES
  // ============================================================================

  /// Effectue une requête HTTP avec gestion d'erreurs et timeout.
  Future<http.Response> _performRequest(
    String method,
    Uri uri, {
    Map<String, String>? headers,
    Object? body,
  }) async {
    AppLogger.http(method, uri.toString());

    try {
      http.Response response;
      final requestHeaders = {..._defaultHeaders, ...?headers};

      switch (method) {
        case 'GET':
          response = await client
              .get(uri, headers: requestHeaders)
              .timeout(_timeout);
          break;
        case 'POST':
          response = await client
              .post(uri, headers: requestHeaders, body: body)
              .timeout(_timeout);
          break;
        case 'PUT':
          response = await client
              .put(uri, headers: requestHeaders, body: body)
              .timeout(_timeout);
          break;
        case 'DELETE':
          response = await client
              .delete(uri, headers: requestHeaders)
              .timeout(_timeout);
          break;
        default:
          throw ArgumentError('Méthode HTTP non supportée: $method');
      }

      AppLogger.http(method, uri.toString(), statusCode: response.statusCode);
      AppLogger.d('Réponse: ${response.body}', tag: 'SocialRemoteDataSource');

      return response;
    } on SocketException catch (e, stackTrace) {
      AppLogger.e('Erreur de connexion réseau', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      throw const ServerException(
        'Erreur de connexion réseau. Vérifiez votre connexion internet.',
      );
    } on http.ClientException catch (e, stackTrace) {
      AppLogger.e('Erreur client HTTP', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      throw ServerException('Erreur client HTTP: ${e.message}');
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la requête', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Parse la réponse JSON et gère les codes de statut HTTP.
  dynamic _parseJsonResponse(http.Response response, List<int> expectedStatusCodes) {
    if (expectedStatusCodes.contains(response.statusCode)) {
      if (response.body.isEmpty) {
        return {};
      }
      return json.decode(response.body);
    } else {
      final errorMessage = (json.decode(response.body) as Map<String, dynamic>?)?['message'] as String? ??
          'Erreur serveur inconnue';

      AppLogger.e('Erreur (${response.statusCode}): $errorMessage', tag: 'SocialRemoteDataSource');

      switch (response.statusCode) {
        case 401:
          throw UnauthorizedException(errorMessage);
        case 404:
          throw ServerException('Post non trouvé: $errorMessage');
        case 409:
          throw ConflictException(errorMessage);
        default:
          throw ServerException('Erreur serveur (${response.statusCode}): $errorMessage');
      }
    }
  }

  // ============================================================================
  // MÉTHODES PUBLIQUES
  // ============================================================================

  /// Récupère tous les posts sociaux.
  ///
  /// [userId] L'identifiant de l'utilisateur (optionnel, pour filtrer)
  ///
  /// Returns une liste de [SocialPostModel]
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<List<SocialPostModel>> getPosts({String? userId}) async {
    AppLogger.d('Récupération des posts', tag: 'SocialRemoteDataSource');

    try {
      final uri = userId != null
          ? Uri.parse(Urls.getSocialPostsByUserId(userId))
          : Uri.parse(Urls.getAllPosts);
      final response = await _performRequest('GET', uri);
      final jsonList = _parseJsonResponse(response, [200]) as List;
      return jsonList.map((json) => SocialPostModel.fromJson(json as Map<String, dynamic>)).toList();
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la récupération des posts', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Récupère les posts de l'utilisateur et de ses amis.
  ///
  /// [userId] L'identifiant de l'utilisateur
  /// [page] Le numéro de la page (0-indexé)
  /// [size] La taille de la page
  ///
  /// Returns une liste de [SocialPostModel]
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<List<SocialPostModel>> getPostsByFriends({
    required String userId,
    int page = 0,
    int size = 20,
  }) async {
    AppLogger.d('Récupération des posts des amis pour $userId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(
        '${Urls.getSocialPostsByFriends(userId)}?page=$page&size=$size',
      );
      final response = await _performRequest('GET', uri);
      final jsonList = _parseJsonResponse(response, [200]) as List;
      return jsonList.map((json) => SocialPostModel.fromJson(json as Map<String, dynamic>)).toList();
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la récupération des posts des amis', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Crée un nouveau post social.
  ///
  /// [content] Le contenu du post
  /// [userId] L'identifiant de l'utilisateur créateur
  /// [imageUrl] URL de l'image (optionnel)
  ///
  /// Returns le [SocialPostModel] créé
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<SocialPostModel> createPost({
    required String content,
    required String userId,
    String? imageUrl,
  }) async {
    AppLogger.i('Création de post pour $userId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.createSocialPost);
      final body = jsonEncode({
        'content': content,
        'userId': userId,
        if (imageUrl != null) 'imageUrl': imageUrl,
      });
      final response = await _performRequest('POST', uri, body: body);
      final jsonResponse = _parseJsonResponse(response, [201]) as Map<String, dynamic>;
      return SocialPostModel.fromJson(jsonResponse);
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la création du post', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Recherche des posts sociaux.
  ///
  /// [query] Le terme de recherche
  ///
  /// Returns une liste de [SocialPostModel] correspondant à la recherche
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<List<SocialPostModel>> searchPosts(String query) async {
    AppLogger.d('Recherche: $query', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.searchSocialPostsWithQuery(query));
      final response = await _performRequest('GET', uri);
      final jsonList = _parseJsonResponse(response, [200]) as List;
      return jsonList.map((json) => SocialPostModel.fromJson(json as Map<String, dynamic>)).toList();
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la recherche', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Like un post.
  ///
  /// [postId] L'ID du post
  ///
  /// Returns le [SocialPostModel] mis à jour
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<SocialPostModel> likePost(String postId) async {
    AppLogger.d('Like du post: $postId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.likeSocialPostWithId(postId));
      final response = await _performRequest('POST', uri);
      final jsonResponse = _parseJsonResponse(response, [200]) as Map<String, dynamic>;
      return SocialPostModel.fromJson(jsonResponse);
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors du like', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Ajoute un commentaire à un post.
  ///
  /// [postId] L'ID du post
  ///
  /// Returns le [SocialPostModel] mis à jour
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<SocialPostModel> commentPost(String postId) async {
    AppLogger.d('Commentaire sur le post: $postId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.commentSocialPostWithId(postId));
      final response = await _performRequest('POST', uri);
      final jsonResponse = _parseJsonResponse(response, [200]) as Map<String, dynamic>;
      return SocialPostModel.fromJson(jsonResponse);
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors du commentaire', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Partage un post.
  ///
  /// [postId] L'ID du post
  ///
  /// Returns le [SocialPostModel] mis à jour
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<SocialPostModel> sharePost(String postId) async {
    AppLogger.d('Partage du post: $postId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.shareSocialPostWithId(postId));
      final response = await _performRequest('POST', uri);
      final jsonResponse = _parseJsonResponse(response, [200]) as Map<String, dynamic>;
      return SocialPostModel.fromJson(jsonResponse);
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors du commentaire', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Supprime un post.
  ///
  /// [postId] L'ID du post
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<void> deletePost(String postId) async {
    AppLogger.i('Suppression du post: $postId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.deleteSocialPostWithId(postId));
      final response = await _performRequest('DELETE', uri);
      _parseJsonResponse(response, [200, 204]);
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la suppression du post', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  // ============================================================================
  // MÉTHODES POUR LES COMMENTAIRES
  // ============================================================================

  /// Récupère tous les commentaires d'un post.
  ///
  /// [postId] L'ID du post
  ///
  /// Returns une liste de [CommentModel]
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<List<CommentModel>> getComments(String postId) async {
    AppLogger.d('Récupération des commentaires pour le post: $postId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.getCommentsForPost(postId));
      final response = await _performRequest('GET', uri);
      final jsonList = _parseJsonResponse(response, [200]) as List;
      return jsonList.map((json) => CommentModel.fromJson(json as Map<String, dynamic>)).toList();
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la récupération des commentaires', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Crée un nouveau commentaire sur un post.
  ///
  /// [postId] L'ID du post
  /// [content] Le contenu du commentaire
  /// [userId] L'ID de l'utilisateur créateur
  ///
  /// Returns le [CommentModel] créé
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<CommentModel> createComment({
    required String postId,
    required String content,
    required String userId,
  }) async {
    AppLogger.i('Création de commentaire pour le post: $postId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse(Urls.commentSocialPostWithId(postId));
      final body = jsonEncode({
        'content': content,
        'userId': userId,
      });
      final response = await _performRequest('POST', uri, body: body);
      final jsonResponse = _parseJsonResponse(response, [200, 201]) as Map<String, dynamic>;
      return CommentModel.fromJson(jsonResponse);
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la création du commentaire', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }

  /// Supprime un commentaire.
  ///
  /// [postId] L'ID du post
  /// [commentId] L'ID du commentaire
  ///
  /// Throws [ServerException] en cas d'erreur
  Future<void> deleteComment(String postId, String commentId) async {
    AppLogger.i('Suppression du commentaire: $commentId', tag: 'SocialRemoteDataSource');

    try {
      final uri = Uri.parse('${Urls.getCommentsForPost(postId)}/$commentId');
      final response = await _performRequest('DELETE', uri);
      _parseJsonResponse(response, [200, 204]);
    } catch (e, stackTrace) {
      AppLogger.e('Erreur lors de la suppression du commentaire', error: e, stackTrace: stackTrace, tag: 'SocialRemoteDataSource');
      rethrow;
    }
  }
}

