import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../models/user.dart';
import '../models/am_registration_data.dart';
import 'api/api_config.dart';
import 'api/tokenService.dart';
import '../utils/nir_utils.dart';

class AuthService {
  static const String _currentUserKey = 'current_user';

  /// Connexion de l'utilisateur
  /// Retourne l'utilisateur connecté avec le flag changement_mdp_obligatoire
  static Future<AppUser> login(String email, String password) async {
    try {
      final response = await http.post(
        Uri.parse('${ApiConfig.baseUrl}${ApiConfig.login}'),
        headers: ApiConfig.headers,
        body: jsonEncode({
          'email': email,
          'password': password,
        }),
      );

      if (response.statusCode == 200 || response.statusCode == 201) {
        final data = jsonDecode(response.body);
        // API renvoie access_token / refresh_token (snake_case)
        final accessToken = data['access_token'] as String? ?? data['accessToken'] as String?;
        final refreshToken = data['refresh_token'] as String? ?? data['refreshToken'] as String?;
        if (accessToken == null) throw Exception('Token absent dans la réponse serveur');

        await TokenService.saveToken(accessToken);
        await TokenService.saveRefreshToken(refreshToken ?? '');

        final user = await _fetchUserProfile(accessToken);
        
        // Stocker l'utilisateur en cache
        await _saveCurrentUser(user);
        
        return user;
      } else {
        final error = jsonDecode(response.body);
        throw Exception(error['message'] ?? 'Erreur de connexion');
      }
    } catch (e) {
      if (e is Exception) rethrow;
      throw Exception('Erreur réseau: impossible de se connecter au serveur');
    }
  }

  /// Récupère le profil utilisateur via /auth/me
  static Future<AppUser> _fetchUserProfile(String token) async {
    try {
      final response = await http.get(
        Uri.parse('${ApiConfig.baseUrl}${ApiConfig.authMe}'),
        headers: ApiConfig.authHeaders(token),
      );

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        return AppUser.fromJson(data);
      } else {
        throw Exception('Erreur lors de la récupération du profil');
      }
    } catch (e) {
      if (e is Exception) rethrow;
      throw Exception('Erreur réseau: impossible de récupérer le profil');
    }
  }

  /// Changement de mot de passe obligatoire
  static Future<void> changePasswordRequired({
    required String currentPassword,
    required String newPassword,
  }) async {
    final token = await TokenService.getToken();
    if (token == null) {
      throw Exception('Non authentifié');
    }

    try {
      final response = await http.post(
        Uri.parse('${ApiConfig.baseUrl}${ApiConfig.changePasswordRequired}'),
        headers: ApiConfig.authHeaders(token),
        body: jsonEncode({
          'mot_de_passe_actuel': currentPassword,
          'nouveau_mot_de_passe': newPassword,
          'confirmation_mot_de_passe': newPassword,
        }),
      );

      if (response.statusCode != 200 && response.statusCode != 201) {
        final error = jsonDecode(response.body);
        throw Exception(error['message'] ?? 'Erreur lors du changement de mot de passe');
      }

      // Après le changement de MDP, rafraîchir le profil utilisateur
      final user = await _fetchUserProfile(token);
      await _saveCurrentUser(user);
    } catch (e) {
      if (e is Exception) rethrow;
      throw Exception('Erreur réseau: impossible de changer le mot de passe');
    }
  }

  /// Déconnexion de l'utilisateur
  static Future<void> logout() async {
    await TokenService.clearAll();
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(_currentUserKey);
  }

  /// Vérifie si l'utilisateur est connecté
  static Future<bool> isLoggedIn() async {
    final token = await TokenService.getToken();
    return token != null;
  }

  /// Récupère l'utilisateur connecté depuis le cache
  static Future<AppUser?> getCurrentUser() async {
    final prefs = await SharedPreferences.getInstance();
    final userJson = prefs.getString(_currentUserKey);
    
    if (userJson != null) {
      return AppUser.fromJson(jsonDecode(userJson));
    }
    
    return null;
  }

  /// Sauvegarde l'utilisateur actuel en cache
  static Future<void> _saveCurrentUser(AppUser user) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(_currentUserKey, jsonEncode(user.toJson()));
  }

  /// Inscription AM complète (POST /auth/register/am).
  /// En cas de succès (201), aucune donnée utilisateur retournée ; rediriger vers login.
  static Future<void> registerAM(AmRegistrationData data) async {
    String? photoBase64;
    if (data.photoPath != null && data.photoPath!.isNotEmpty && !data.photoPath!.startsWith('assets/')) {
      try {
        final file = File(data.photoPath!);
        if (await file.exists()) {
          final bytes = await file.readAsBytes();
          photoBase64 = 'data:image/jpeg;base64,${base64Encode(bytes)}';
        }
      } catch (_) {}
    }

    final body = {
      'email': data.email,
      'prenom': data.firstName,
      'nom': data.lastName,
      'telephone': data.phone,
      'adresse': data.streetAddress.isNotEmpty ? data.streetAddress : null,
      'code_postal': data.postalCode.isNotEmpty ? data.postalCode : null,
      'ville': data.city.isNotEmpty ? data.city : null,
      if (photoBase64 != null) 'photo_base64': photoBase64,
      'consentement_photo': data.photoConsent,
      'date_naissance': data.dateOfBirth != null
          ? '${data.dateOfBirth!.year}-${data.dateOfBirth!.month.toString().padLeft(2, '0')}-${data.dateOfBirth!.day.toString().padLeft(2, '0')}'
          : null,
      'lieu_naissance_ville': data.birthCity.isNotEmpty ? data.birthCity : null,
      'lieu_naissance_pays': data.birthCountry.isNotEmpty ? data.birthCountry : null,
      'nir': normalizeNir(data.nir),
      'numero_agrement': data.agrementNumber,
      'capacite_accueil': data.capacity ?? 1,
      'biographie': data.presentationText.isNotEmpty ? data.presentationText : null,
      'acceptation_cgu': data.cguAccepted,
      'acceptation_privacy': data.cguAccepted,
    };

    final response = await http.post(
      Uri.parse('${ApiConfig.baseUrl}${ApiConfig.registerAM}'),
      headers: ApiConfig.headers,
      body: jsonEncode(body),
    );

    if (response.statusCode == 200 || response.statusCode == 201) {
      return;
    }

    final decoded = response.body.isNotEmpty ? jsonDecode(response.body) : null;
    final message = _extractErrorMessage(decoded, response.statusCode);
    throw Exception(message);
  }

  /// Extrait le message d'erreur des réponses NestJS (message string, array, ou objet).
  static String _extractErrorMessage(dynamic decoded, int statusCode) {
    const fallback = 'Erreur lors de l\'inscription';
    if (decoded == null || decoded is! Map) return '$fallback ($statusCode)';
    final msg = decoded['message'];
    if (msg == null) return decoded['error'] as String? ?? '$fallback ($statusCode)';
    if (msg is String) return msg;
    if (msg is List) return msg.map((e) => e.toString()).join('. ').trim();
    if (msg is Map && msg['message'] != null) return msg['message'].toString();
    return '$fallback ($statusCode)';
  }

  /// Rafraîchit le profil utilisateur depuis l'API
  static Future<AppUser?> refreshCurrentUser() async {
    final token = await TokenService.getToken();
    if (token == null) return null;

    try {
      final user = await _fetchUserProfile(token);
      await _saveCurrentUser(user);
      return user;
    } catch (e) {
      return null;
    }
  }
} 