/// Datasource distant pour la messagerie v4 — /api/messagerie/*
library messaging_remote_datasource;

import 'dart:convert';

import 'package:injectable/injectable.dart';

import '../../../../core/config/environment.dart';
import '../../../../core/error/exceptions.dart';
import '../../../authentication/data/datasources/keycloak_auth_service.dart';
import '../models/conversation_model.dart';
import '../models/message_model.dart';
import '../models/contact_policy_model.dart';

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

@lazySingleton
class MessagingRemoteDatasource {
  final http.Client client;
  final KeycloakAuthService authService;

  MessagingRemoteDatasource({
    required this.client,
    required this.authService,
  });

  /// Headers HTTP avec authentification — rafraîchit le token si expiré
  Future<Map<String, String>> _getHeaders() async {
    final token = await authService.getValidAccessToken();
    return {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      if (token != null) 'Authorization': 'Bearer $token',
    };
  }

  String get _base => '${AppConfig.apiBaseUrl}/api/messagerie';

  // ── Conversations ─────────────────────────────────────────────────────────

  Future<List<ConversationSummaryModel>> getMesConversations() async {
    final response = await client.get(
      Uri.parse('$_base/conversations'),
      headers: await _getHeaders(),
    );

    _checkAuth(response);
    if (response.statusCode == 200) {
      final list = json.decode(response.body) as List<dynamic>;
      return list
          .map((j) => ConversationSummaryModel.fromJson(j as Map<String, dynamic>))
          .toList();
    }
    throw ServerException('Erreur récupération conversations (${response.statusCode})');
  }

  Future<ConversationModel> getConversation(String id) async {
    final response = await client.get(
      Uri.parse('$_base/conversations/$id'),
      headers: await _getHeaders(),
    );

    _checkAuth(response);
    if (response.statusCode == 200) {
      return ConversationModel.fromJson(json.decode(response.body) as Map<String, dynamic>);
    }
    if (response.statusCode == 404) throw NotFoundException('Conversation non trouvée');
    throw ServerException('Erreur récupération conversation (${response.statusCode})');
  }

  Future<ConversationModel> demarrerConversationDirecte({
    required String destinataireId,
    required String organisationId,
    String? premierMessage,
  }) async {
    final body = json.encode({
      'destinataireId': destinataireId,
      'organisationId': organisationId,
      if (premierMessage != null) 'premierMessage': premierMessage,
    });

    final response = await client.post(
      Uri.parse('$_base/conversations/directe'),
      headers: await _getHeaders(),
      body: body,
    );

    _checkAuth(response);
    if (response.statusCode == 201 || response.statusCode == 200) {
      return ConversationModel.fromJson(json.decode(response.body) as Map<String, dynamic>);
    }
    throw ServerException('Erreur démarrage conversation directe (${response.statusCode})');
  }

  Future<ConversationModel> demarrerConversationRole({
    required String roleCible,
    required String organisationId,
    String? premierMessage,
  }) async {
    final body = json.encode({
      'roleCible': roleCible,
      'organisationId': organisationId,
      if (premierMessage != null) 'premierMessage': premierMessage,
    });

    final response = await client.post(
      Uri.parse('$_base/conversations/role'),
      headers: await _getHeaders(),
      body: body,
    );

    _checkAuth(response);
    if (response.statusCode == 201 || response.statusCode == 200) {
      return ConversationModel.fromJson(json.decode(response.body) as Map<String, dynamic>);
    }
    throw ServerException('Erreur démarrage conversation rôle (${response.statusCode})');
  }

  Future<void> archiverConversation(String id) async {
    final response = await client.delete(
      Uri.parse('$_base/conversations/$id'),
      headers: await _getHeaders(),
    );

    _checkAuth(response);
    if (response.statusCode != 200 && response.statusCode != 204) {
      throw ServerException('Erreur archivage conversation (${response.statusCode})');
    }
  }

  // ── Messages ──────────────────────────────────────────────────────────────

  Future<MessageModel> envoyerMessage(
    String conversationId, {
    required String typeMessage,
    String? contenu,
    String? urlFichier,
    int? dureeAudio,
    String? messageParentId,
  }) async {
    final body = json.encode({
      'typeMessage': typeMessage,
      if (contenu != null) 'contenu': contenu,
      if (urlFichier != null) 'urlFichier': urlFichier,
      if (dureeAudio != null) 'dureeAudio': dureeAudio,
      if (messageParentId != null) 'messageParentId': messageParentId,
    });

    final response = await client.post(
      Uri.parse('$_base/conversations/$conversationId/messages'),
      headers: await _getHeaders(),
      body: body,
    );

    _checkAuth(response);
    if (response.statusCode == 201 || response.statusCode == 200) {
      return MessageModel.fromJson(json.decode(response.body) as Map<String, dynamic>);
    }
    throw ServerException('Erreur envoi message (${response.statusCode})');
  }

  Future<List<MessageModel>> getMessages(String conversationId, {int page = 0}) async {
    final uri = Uri.parse('$_base/conversations/$conversationId/messages')
        .replace(queryParameters: {'page': page.toString()});

    final response = await client.get(uri, headers: await _getHeaders());

    _checkAuth(response);
    if (response.statusCode == 200) {
      final list = json.decode(response.body) as List<dynamic>;
      return list
          .map((j) => MessageModel.fromJson(j as Map<String, dynamic>))
          .toList();
    }
    throw ServerException('Erreur récupération messages (${response.statusCode})');
  }

  Future<void> marquerLu(String conversationId) async {
    final response = await client.put(
      Uri.parse('$_base/conversations/$conversationId/lire'),
      headers: await _getHeaders(),
    );

    _checkAuth(response);
    if (response.statusCode != 200 && response.statusCode != 204) {
      throw ServerException('Erreur marquage lu (${response.statusCode})');
    }
  }

  Future<void> supprimerMessage(String conversationId, String messageId) async {
    final response = await client.delete(
      Uri.parse('$_base/conversations/$conversationId/messages/$messageId'),
      headers: await _getHeaders(),
    );

    _checkAuth(response);
    if (response.statusCode != 200 && response.statusCode != 204) {
      throw ServerException('Erreur suppression message (${response.statusCode})');
    }
  }

  // ── Blocages ──────────────────────────────────────────────────────────────

  Future<void> bloquerMembre({
    required String membreABloquerId,
    String? organisationId,
    String? raison,
  }) async {
    final body = json.encode({
      'membreABloquerId': membreABloquerId,
      if (organisationId != null) 'organisationId': organisationId,
      if (raison != null) 'raison': raison,
    });

    final response = await client.post(
      Uri.parse('$_base/blocages'),
      headers: await _getHeaders(),
      body: body,
    );

    _checkAuth(response);
    if (response.statusCode != 200 && response.statusCode != 204) {
      throw ServerException('Erreur blocage membre (${response.statusCode})');
    }
  }

  Future<void> debloquerMembre(String membreId, {String? organisationId}) async {
    final uri = Uri.parse('$_base/blocages/$membreId').replace(
      queryParameters: {
        if (organisationId != null) 'organisationId': organisationId,
      },
    );

    final response = await client.delete(uri, headers: await _getHeaders());

    _checkAuth(response);
    if (response.statusCode != 200 && response.statusCode != 204) {
      throw ServerException('Erreur déblocage membre (${response.statusCode})');
    }
  }

  Future<List<Map<String, dynamic>>> getMesBlocages() async {
    final response = await client.get(
      Uri.parse('$_base/blocages'),
      headers: await _getHeaders(),
    );

    _checkAuth(response);
    if (response.statusCode == 200) {
      final list = json.decode(response.body) as List<dynamic>;
      return list.map((j) => j as Map<String, dynamic>).toList();
    }
    throw ServerException('Erreur récupération blocages (${response.statusCode})');
  }

  // ── Politique de communication ────────────────────────────────────────────

  Future<ContactPolicyModel> getPolitique(String organisationId) async {
    final response = await client.get(
      Uri.parse('$_base/politique/$organisationId'),
      headers: await _getHeaders(),
    );

    _checkAuth(response);
    if (response.statusCode == 200) {
      return ContactPolicyModel.fromJson(json.decode(response.body) as Map<String, dynamic>);
    }
    throw ServerException('Erreur récupération politique (${response.statusCode})');
  }

  Future<ContactPolicyModel> mettreAJourPolitique(
    String organisationId, {
    required String typePolitique,
    required bool autoriserMembreVersMembre,
    required bool autoriserMembreVersRole,
    required bool autoriserNotesVocales,
  }) async {
    final body = json.encode({
      'typePolitique': typePolitique,
      'autoriserMembreVersMembre': autoriserMembreVersMembre,
      'autoriserMembreVersRole': autoriserMembreVersRole,
      'autoriserNotesVocales': autoriserNotesVocales,
    });

    final response = await client.put(
      Uri.parse('$_base/politique/$organisationId'),
      headers: await _getHeaders(),
      body: body,
    );

    _checkAuth(response);
    if (response.statusCode == 200) {
      return ContactPolicyModel.fromJson(json.decode(response.body) as Map<String, dynamic>);
    }
    throw ServerException('Erreur mise à jour politique (${response.statusCode})');
  }

  // ── Helpers ────────────────────────────────────────────────────────────────

  void _checkAuth(http.Response response) {
    if (response.statusCode == 401) throw UnauthorizedException();
    if (response.statusCode == 403) throw ForbiddenException('Permission insuffisante');
  }
}
