import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart';
import 'package:taxglide/consts/local_store.dart';
import 'package:taxglide/controller/api_consts.dart';
import 'package:taxglide/model/chat_model.dart';
import 'package:taxglide/model/chat_profile_model.dart';
import 'package:taxglide/model/city_model.dart';
import 'package:taxglide/model/count_model.dart';
import 'package:taxglide/model/country_model.dart';
import 'package:taxglide/model/dash_board_model.dart';
import 'package:taxglide/model/detail_model.dart';
import 'package:taxglide/model/employeeprofile_model.dart';
import 'package:taxglide/model/login_model.dart';
import 'package:taxglide/model/notification_model.dart';
import 'package:taxglide/model/notificationcount_model.dart';
import 'package:taxglide/model/profile_get_model.dart';
import 'package:taxglide/model/serivce_list_model.dart';
import 'package:taxglide/model/service_list_history_model.dart';
import 'package:taxglide/model/signup_model.dart';
import 'package:taxglide/model/staff_model.dart';
import 'package:taxglide/model/terms_model.dart';
import 'package:taxglide/router/consts_routers.dart';

class ApiRepository {
  final LocalStore _localStore = LocalStore();
  final FirebaseMessaging _messaging = FirebaseMessaging.instance;

  // 🔹 Common headers without token (for login/signup)
  Map<String, String> get _baseHeaders => {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  // 🔹 Authorized headers with token
  Future<Map<String, String>> _authorizedHeaders() async {
    final token = await _localStore.getToken();
    return {
      'Authorization': 'Bearer $token',
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    };
  }

  /// 🔑 Get fresh FCM token on-demand (not from storage)
  Future<String?> _getFreshFcmToken() async {
    try {
      debugPrint('🔄 Generating fresh FCM token...');
      final token = await _messaging.getToken();
      debugPrint('✅ FCM Token generated: $token');
      return token;
    } catch (e) {
      debugPrint('❌ Error getting FCM token: $e');
      return null;
    }
  }

  Future<Map<String, String?>> _regenerateFcmToken() async {
    try {
      // 📌 Print old token BEFORE deleting
      final oldToken = await _messaging.getToken();
      debugPrint('📋 OLD FCM Token (before delete): $oldToken');

      debugPrint('🔄 Deleting old FCM token...');

      // Delete the current token
      await _messaging.deleteToken();
      debugPrint('🗑️ Old FCM token deleted');

      // Wait for Firebase to process the deletion
      await Future.delayed(const Duration(milliseconds: 800));

      // Request permission again (important for iOS)
      await _messaging.requestPermission();

      // Generate new tokens
      debugPrint('🔄 Requesting new tokens...');
      final newToken = await _messaging.getToken();
      final apnsToken = await _messaging.getAPNSToken();

      debugPrint('✅ NEW FCM Token: $newToken');
      debugPrint('✅ NEW APNS Token: $apnsToken');

      // Compare tokens
      if (oldToken == newToken) {
        debugPrint('⚠️ WARNING: Old and New tokens are SAME!');
      } else {
        debugPrint('✅ SUCCESS: New token is DIFFERENT from old token');
      }

      return {'fcm_token': newToken, 'apns_token': apnsToken};
    } catch (e) {
      debugPrint('❌ Error regenerating tokens: $e');
      return {'fcm_token': null, 'apns_token': null};
    }
  }

  // 🔐 Centralized token expiry handler
  Future<void> _handleUnauthorized() async {
    debugPrint('🔐 Token expired - Auto logout');
    await _localStore.clearAll();
    Get.offAllNamed(ConstRouters.login);
    Get.snackbar(
      'Session Expired',
      'Please login again',
      snackPosition: SnackPosition.BOTTOM,
      backgroundColor: Colors.red,
      colorText: Colors.white,
      duration: const Duration(seconds: 3),
    );
  }

  // 🔹 Wrapper to handle token expiry for all authenticated requests
  Future<http.Response> _makeAuthenticatedRequest(
    Future<http.Response> Function() request,
  ) async {
    try {
      final response = await request();

      if (response.statusCode == 401 || response.statusCode == 403) {
        await _handleUnauthorized();
        throw Exception('Unauthorized - Token expired');
      }

      return response;
    } catch (e) {
      rethrow;
    }
  }

  // 🔹 LOGIN API - sends OTP
  Future<Map<String, dynamic>> loginUser(LoginModel model) async {
    try {
      final params = model.toJsonMobile();
      final uri = Uri.parse(ConstsApi.login).replace(
        queryParameters: params.map(
          (key, value) => MapEntry(key, value.toString()),
        ),
      );

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 DIRECT LOGIN (Email + PIN) - ✅ Regenerates FCM token
  Future<Map<String, dynamic>> loginWithPin(String email, String pin) async {
    try {
      final params = {'email': email, 'pin': pin};

      // ✅ Regenerate FCM and APNS tokens on every login
      final tokens = await _regenerateFcmToken();
      final fcmToken = tokens['fcm_token'];
      final apnsToken = tokens['apns_token'];

      if (fcmToken != null && fcmToken.isNotEmpty) {
        params['fcm_token'] = fcmToken;
      }
      if (apnsToken != null && apnsToken.isNotEmpty) {
        params['apns_token'] = apnsToken;
      }

      if (params['fcm_token'] == null) {
        debugPrint('⚠️ Failed to generate FCM token, trying fallback...');
        final fallbackToken = await _getFreshFcmToken();
        if (fallbackToken != null && fallbackToken.isNotEmpty) {
          params['fcm_token'] = fallbackToken;
        }
      }

      final uri = Uri.parse(ConstsApi.loginWithPin).replace(
        queryParameters: params.map(
          (key, value) => MapEntry(key, value.toString()),
        ),
      );

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        await _localStore.saveLoginData(data);
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 VERIFY OTP (LOGIN) - ✅ Regenerates FCM token
  Future<Map<String, dynamic>> verifyOtp(LoginModel model) async {
    try {
      final params = model.toJsonOtp();

      // ✅ Regenerate FCM and APNS tokens on every login
      final tokens = await _regenerateFcmToken();
      final fcmToken = tokens['fcm_token'];
      final apnsToken = tokens['apns_token'];

      if (fcmToken != null && fcmToken.isNotEmpty) {
        params['fcm_token'] = fcmToken;
      }
      if (apnsToken != null && apnsToken.isNotEmpty) {
        params['apns_token'] = apnsToken;
      }

      if (params['fcm_token'] == null) {
        debugPrint('⚠️ Failed to generate FCM token, trying fallback...');
        final fallbackToken = await _getFreshFcmToken();
        if (fallbackToken != null && fallbackToken.isNotEmpty) {
          params['fcm_token'] = fallbackToken;
        }
      }

      final uri = Uri.parse(ConstsApi.verifyOtp).replace(
        queryParameters: params.map(
          (key, value) => MapEntry(key, value.toString()),
        ),
      );

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        await _localStore.saveLoginData(data);
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 EMPLOYEE LOGIN
  Future<Map<String, dynamic>> employeeLogin(String mobile) async {
    try {
      final uri = Uri.parse(
        ConstsApi.employeelogin,
      ).replace(queryParameters: {'mobile': mobile});

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 EMPLOYEE LOGIN (Email + PIN) - ✅ Regenerates FCM token
  Future<Map<String, dynamic>> employeeLoginWithPin(
    String email,
    String pin,
  ) async {
    try {
      final params = {'email': email, 'pin': pin};

      // ✅ Regenerate FCM and APNS tokens on every login
      final tokens = await _regenerateFcmToken();
      final fcmToken = tokens['fcm_token'];
      final apnsToken = tokens['apns_token'];

      if (fcmToken != null && fcmToken.isNotEmpty) {
        params['fcm_token'] = fcmToken;
      }
      if (apnsToken != null && apnsToken.isNotEmpty) {
        params['apns_token'] = apnsToken;
      }

      if (params['fcm_token'] == null) {
        debugPrint('⚠️ Failed to generate FCM token, trying fallback...');
        final fallbackToken = await _getFreshFcmToken();
        if (fallbackToken != null && fallbackToken.isNotEmpty) {
          params['fcm_token'] = fallbackToken;
        }
      }

      final uri = Uri.parse(ConstsApi.employeeLoginWithPin).replace(
        queryParameters: params.map(
          (key, value) => MapEntry(key, value.toString()),
        ),
      );

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        await _localStore.saveLoginData(data);
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 FORGOT PASSWORD REQUEST (Email OTP)
  Future<Map<String, dynamic>> requestForgotPasswordOtp(
    String email,
    bool isEmployee,
  ) async {
    try {
      final endpoint = isEmployee
          ? ConstsApi.employeeForgotPassword
          : ConstsApi.forgotPassword;
      final uri = Uri.parse(endpoint).replace(queryParameters: {'email': email});

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 VERIFY EMAIL OTP (Generic)
  Future<Map<String, dynamic>> verifyEmailOtp(String email, String otp) async {
    try {
      final fcmToken = await _getFreshFcmToken();
      final uri = Uri.parse(ConstsApi.verifyOtp).replace(
        queryParameters: {
          'email': email,
          'otp': otp,
          if (fcmToken != null) 'fcm_token': fcmToken,
        },
      );

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        // ✅ CRITICAL: Save login data to get the token for subsequent requests (like set_pin)
        await _localStore.saveLoginData(data);
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 CHANGE/SET PIN
  Future<Map<String, dynamic>> changePin(String pin) async {
    try {
      final token = await _localStore.getToken();
      final uri = Uri.parse(ConstsApi.changePin).replace(
        queryParameters: {'otp': pin},
      );

      final response = await http.post(
        uri,
        headers: {
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
        },
      );
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }
  // 🔹 SIGNUP API
  Future<Map<String, dynamic>> signupUser(SignupModel model) async {
    try {
      final params = model.toJson();
      final uri = Uri.parse(ConstsApi.signup).replace(
        queryParameters: params.map(
          (key, value) => MapEntry(key, value.toString()),
        ),
      );

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 VERIFY OTP (SIGNUP) - ✅ Regenerates FCM token
  Future<Map<String, dynamic>> verifySignupOtp(
    String email,
    String otp,
  ) async {
    try {
      // ✅ Regenerate FCM and APNS tokens on signup
      final tokens = await _regenerateFcmToken();
      final fcmToken = tokens['fcm_token'];
      final apnsToken = tokens['apns_token'];

      final params = {'email': email, 'otp': otp};

      if (fcmToken != null && fcmToken.isNotEmpty) {
        params['fcm_token'] = fcmToken;
      }
      if (apnsToken != null && apnsToken.isNotEmpty) {
        params['apns_token'] = apnsToken;
      }

      if (params['fcm_token'] == null) {
        debugPrint('⚠️ Failed to generate FCM token, trying fallback...');
        final fallbackToken = await _getFreshFcmToken();
        if (fallbackToken != null && fallbackToken.isNotEmpty) {
          params['fcm_token'] = fallbackToken;
        }
      }

      final uri = Uri.parse(
        ConstsApi.verifyOtp,
      ).replace(queryParameters: params);

      final response = await http.post(uri, headers: _baseHeaders);
      final data = jsonDecode(response.body);

      if (response.statusCode == 200 || response.statusCode == 201) {
        await _localStore.saveLoginData(data);
        return {'success': true, 'data': data};
      } else {
        return {'success': false, 'error': _extractErrorMessage(data)};
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Connection error. Please check your internet.',
      };
    }
  }

  // 🔹 FETCH TERMS & CONDITIONS
  Future<TermsModel> fetchTermsAndConditions() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.terms),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return TermsModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch terms and conditions');
    }
  }

  // 🔹 FETCH DASHBOARD
  Future<DashBoardModel> fetchDashboard() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.dashboard),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return DashBoardModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch dashboard');
    }
  }

  // 🔹 FETCH POLICY
  Future<TermsModel> fetchpolicy() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.policy),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return TermsModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch Policy');
    }
  }

  // 🔹 FETCH PROFILE
  Future<ProfileGetModel> fetchProfile() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.getprofile),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return ProfileGetModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch Profile');
    }
  }

  // 🔹 FETCH EMPLOYEE PROFILE
  Future<EmployeeProfileModel> fetchEmployeeProfile() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.getprofile),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return EmployeeProfileModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch Employee Profile');
    }
  }

  // 🔹 FETCH SERVICE HISTORY
  Future<ServiceListHistoryModel> fetchServiceHistory(
    String type, {
    String? fromDate,
    String? toDate,
  }) async {
    final uri = Uri.parse(ConstsApi.serivcehistory).replace(
      queryParameters: {
        'type': type,
        if (fromDate != null) 'from_date': fromDate,
        if (toDate != null) 'to_date': toDate,
      },
    );

    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(uri, headers: await _authorizedHeaders());
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return ServiceListHistoryModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch Service History');
    }
  }

  // 🔹 FETCH COUNT
  Future<CountModel> fetchCount(String type) async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse("${ConstsApi.count}?chat_id=$type"),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return CountModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch count');
    }
  }

  // 🔹 FETCH NOTIFICATION COUNT
  Future<NotificationCountModel> fetchNotificationCount() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.notificationcount),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return NotificationCountModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch notification count');
    }
  }

  // 🔹 FETCH SERVICE DETAIL
  Future<DetailModel> fetchServiceDetail(int id) async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse("${ConstsApi.serivcedetails}?service_request_id=$id"),
        headers: await _authorizedHeaders(),
      );
    });
    debugPrint("response: ${response.body}");
    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return DetailModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch Service Detail');
    }
  }

  // 🔹 FETCH CHAT DOCUMENTS
  Future<ChatProfileModel> fetchChatDocuments(int id) async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse("${ConstsApi.chatdocument}?chat_id=$id"),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return ChatProfileModel.fromJson(data);
    } else {
      throw Exception('Failed to fetch Chat Documents');
    }
  }

  // 🔹 FETCH SERVICE LIST
  Future<List<ServiceListModel>> fetchServiceList() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.serivcelist),
        headers: await _authorizedHeaders(),
      );
    });

    debugPrint("📡 Status Code: ${response.statusCode}");
    debugPrint("📡 Response: ${response.body}");

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      final List<dynamic> list = data['data'] ?? [];
      return list.map((e) => ServiceListModel.fromJson(e)).toList();
    }

    throw Exception('Failed to fetch service list');
  }

  // 🔹 FETCH STAFF LIST
  Future<List<StaffModel>> fetchStaffList() async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse(ConstsApi.stafflist),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      final List<dynamic> list = data['data'] ?? [];
      return list.map((e) => StaffModel.fromJson(e)).toList();
    } else {
      throw Exception('Failed to fetch staff list');
    }
  }

  // 🔹 FETCH NOTIFICATION LIST
  Future<List<NotificationModel>> fetchNotificationList({
    required int page,
  }) async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse("${ConstsApi.notificationList}?page=$page"),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      final List<dynamic> list = data['data'] ?? [];
      return list.map((e) => NotificationModel.fromJson(e)).toList();
    } else {
      throw Exception('Failed to fetch notifications');
    }
  }

  // 🔹 FETCH CHAT MESSAGES
  Future<List<MessageModel>> fetchChatMessages({
    required int chatId,
    required int page,
  }) async {
    final response = await _makeAuthenticatedRequest(() async {
      return await http.get(
        Uri.parse("${ConstsApi.chatList}/$chatId/messages?page=$page"),
        headers: await _authorizedHeaders(),
      );
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      final messages = data["messages"]["data"] ?? [];
      return List<MessageModel>.from(
        messages.map((e) => MessageModel.fromJson(e)),
      );
    } else {
      throw Exception("Failed to fetch chat messages → ${response.statusCode}");
    }
  }

  // 🔹 FETCH COUNTRY AND STATES
  Future<CountryModel> fetchCountryAndStates(String url) async {
    try {
      debugPrint('🌐 Fetching Country & States from: $url');

      final response = await _makeAuthenticatedRequest(() async {
        return await http.get(
          Uri.parse(url),
          headers: await _authorizedHeaders(),
        );
      });

      debugPrint('📡 API Status: ${response.statusCode}');
      debugPrint('📡 API Body: ${response.body}');

      if (response.statusCode == 200) {
        final jsonBody = jsonDecode(response.body);
        final model = CountryModel.fromJson(jsonBody);
        debugPrint(
          '✅ Countries: ${model.countriesData?.length ?? 0}, States: ${model.statesData?.length ?? 0}',
        );
        return model;
      } else {
        final error = jsonDecode(response.body);
        throw Exception(error['message'] ?? 'Failed to fetch data');
      }
    } catch (e) {
      debugPrint('❌ fetchCountryAndStates Error: $e');
      throw Exception('Error fetching country & state data: $e');
    }
  }

  // 🔹 FETCH CITY BY STATE
  Future<CityModel> fetchCityByState(String url, int stateId) async {
    try {
      final fullUrl = "$url?state_id=$stateId";
      debugPrint('🌐 Fetching Cities (Districts) from: $fullUrl');

      final response = await _makeAuthenticatedRequest(() async {
        return await http.get(
          Uri.parse(fullUrl),
          headers: await _authorizedHeaders(),
        );
      });

      debugPrint('📡 City API Status: ${response.statusCode}');
      debugPrint('📡 City API Body: ${response.body}');

      if (response.statusCode == 200) {
        final jsonBody = jsonDecode(response.body);
        final model = CityModel.fromJson(jsonBody);
        debugPrint('✅ Districts fetched: ${model.districtsData?.length ?? 0}');
        return model;
      } else {
        final error = jsonDecode(response.body);
        throw Exception(error['message'] ?? 'Failed to fetch city data');
      }
    } catch (e) {
      debugPrint('❌ fetchCityByState Error: $e');
      throw Exception('Error fetching city data: $e');
    }
  }

  // 🔹 KYC FORM UPDATE
  Future<void> kycFormUpdate({
    File? logo,
    File? panFile,
    File? gstFile,
    File? incorporationFile,
    int? countryId,
    int? stateId,
    int? cityId,
    String? companyPincode,
    String? companyAddress,
    String? panNumber,
    String? gstNumber,
    String? tanNumber,
    String? cinNumber,
    String? yearOfIncorporation,
    String? address,
  }) async {
    try {
      final token = await _localStore.getToken();
      final uri = Uri.parse(ConstsApi.kycupdate);

      var request = http.MultipartRequest('POST', uri)
        ..headers.addAll({
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
          'Connection': 'keep-alive',
        })
        ..fields.addAll({
          if (countryId != null) 'country_id': countryId.toString(),
          if (stateId != null) 'state_id': stateId.toString(),
          if (cityId != null) 'district_id': cityId.toString(),
          if (companyPincode != null) 'company_pincode': companyPincode,
          if (companyAddress != null) 'company_address': companyAddress,
          if (panNumber != null) 'pan_number': panNumber,
          if (gstNumber != null) 'gst_number': gstNumber,
          if (tanNumber != null) 'tan_number': tanNumber,
          if (cinNumber != null) 'cin': cinNumber,
          if (yearOfIncorporation != null) 'year_of_incorporation': yearOfIncorporation,
          if (address != null) 'address': address,
        });

      await Future.wait([
        _addFileIfExists(request, logo, 'logo'),
        _addFileIfExists(request, panFile, 'pan_file'),
        _addFileIfExists(request, gstFile, 'gst_file'),
        _addFileIfExists(request, incorporationFile, 'incorporation_file'),
      ]);

      final streamedResponse = await request.send().timeout(
        const Duration(seconds: 60),
        onTimeout: () {
          throw TimeoutException('Request timed out after 60 seconds');
        },
      );

      final response = await http.Response.fromStream(streamedResponse);
debugPrint('📦 KYC Response Status: ${response.statusCode}');
debugPrint('📦 KYC Response Body: ${response.body}');
      // Handle token expiry
      if (response.statusCode == 401 || response.statusCode == 403) {
        await _handleUnauthorized();
        throw Exception('Unauthorized - Token expired');
      }

      if (response.statusCode == 200) {
        debugPrint('✅ KYC update success');
        return;
      }

      final decoded = _safeJsonDecode(response.body);

      if (response.statusCode == 422) {
        throw _formatValidationErrors(decoded);
      } else {
        final message =
            decoded?['message'] ?? decoded?['error'] ?? 'Something went wrong';
        throw {'error': message.toString()};
      }
    } on SocketException {
      throw {'error': 'No internet connection. Please check your network.'};
    } on TimeoutException {
      throw {'error': 'Request timed out. Please try again.'};
    } on FormatException {
      throw {'error': 'Invalid server response. Please try again.'};
    } catch (e) {
      if (e is Map<String, dynamic>) rethrow;
      debugPrint('❌ KYC update error: $e');
      throw {'error': 'Failed to update KYC: ${e.toString()}'};
    }
  }

  // 🔹 SEND CHAT MESSAGE
  Future<void> sendChatMessage({
    required int chatId,
    required String messages,
    int? tagId,
    List<File>? files,
  }) async {
    try {
      final token = await _localStore.getToken();
      final uri = Uri.parse(ConstsApi.sendChatMessage);

      var request = http.MultipartRequest('POST', uri)
        ..headers.addAll({
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
        })
        ..fields.addAll({'chat_id': chatId.toString(), 'message': messages});

      if (tagId != null && tagId > 0) {
        request.fields['tag_id'] = tagId.toString();
      }

      if (files != null && files.isNotEmpty) {
        for (int i = 0; i < files.length; i++) {
          request.files.add(
            await http.MultipartFile.fromPath(
              'file[$i]',
              files[i].path,
              filename: files[i].path.split('/').last,
            ),
          );
        }
      }

      final streamedResponse = await request.send();
      final response = await http.Response.fromStream(streamedResponse);

      // Handle token expiry
      if (response.statusCode == 401 || response.statusCode == 403) {
        await _handleUnauthorized();
        throw Exception('Unauthorized - Token expired');
      }

      final decoded = _safeJsonDecode(response.body);

      if (response.statusCode == 200) {
        debugPrint("✅ Chat message sent successfully");
        return;
      }

      if (response.statusCode == 422) {
        throw _formatValidationErrors(decoded);
      } else {
        final message =
            decoded?['message'] ?? decoded?['error'] ?? 'Something went wrong';
        throw {'error': message.toString()};
      }
    } catch (e) {
      debugPrint('❌ Chat send error: $e');
      if (e is Map<String, dynamic>) rethrow;
      throw {'error': 'Failed to send message: ${e.toString()}'};
    }
  }

  // 🔹 UPDATE SERVICE REQUEST
  Future<void> updateServiceRequest({
    required int serviceId,
    required String message,
    List<File>? documents,
  }) async {
    try {
      final token = await _localStore.getToken();
      final uri = Uri.parse(ConstsApi.serivcerequest);

      var request = http.MultipartRequest('POST', uri)
        ..headers.addAll({
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
          'Connection': 'keep-alive',
        })
        ..fields.addAll({
          'service_id': serviceId.toString(),
          'message': message,
        });

      if (documents != null && documents.isNotEmpty) {
        for (int i = 0; i < documents.length; i++) {
          final file = documents[i];
          final fileName = file.path.split('/').last;
          request.files.add(
            await http.MultipartFile.fromPath(
              'documents[$i]',
              file.path,
              filename: fileName,
            ),
          );
        }
      }

      final streamedResponse = await request.send().timeout(
        const Duration(seconds: 60),
        onTimeout: () {
          throw TimeoutException('Request timed out after 60 seconds');
        },
      );

      final response = await http.Response.fromStream(streamedResponse);

      // Handle token expiry
      if (response.statusCode == 401 || response.statusCode == 403) {
        await _handleUnauthorized();
        throw Exception('Unauthorized - Token expired');
      }

      if (response.statusCode == 200) {
        debugPrint('✅ Service request updated successfully');
        return;
      }

      final decoded = _safeJsonDecode(response.body);
      if (response.statusCode == 422) {
        throw _formatValidationErrors(decoded);
      } else {
        final message =
            decoded?['message'] ?? decoded?['error'] ?? 'Something went wrong';
        throw {'error': message.toString()};
      }
    } on SocketException {
      throw {'error': 'No internet connection. Please check your network.'};
    } on TimeoutException {
      throw {'error': 'Request timed out. Please try again.'};
    } on FormatException {
      throw {'error': 'Invalid server response. Please try again.'};
    } catch (e) {
      if (e is Map<String, dynamic>) rethrow;
      debugPrint('❌ Service request update error: $e');
      throw {'error': 'Failed to update service request: ${e.toString()}'};
    }
  }

  Future<Map<String, dynamic>> payNow({required int id}) async {
    try {
      final token = await _localStore.getToken();
      final uri = Uri.parse(ConstsApi.payNow).replace(
        queryParameters: {'id': id.toString()},
      );

      final response = await http.post(
        uri,
        headers: {
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
        },
      );

      final data = jsonDecode(response.body);
      debugPrint("🚀 Pay Now API Response: $data");
      print("🚀 Pay Now API Response: $data"); // Force print as well

      if (response.statusCode != 200 && response.statusCode != 201) {
        throw data['message'] ?? 'Failed to process payment';
      }

      return data;
    } catch (e) {
      debugPrint('❌ Pay Now API Error: $e');
      rethrow;
    }
  }

  Future<void> proformaaccept({required int proformaId}) async {
    try {
      final token = await _localStore.getToken();
      final uri = Uri.parse(ConstsApi.proformaaccept);

      var request = http.MultipartRequest('POST', uri)
        ..headers.addAll({
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
          'Connection': 'keep-alive',
        })
        ..fields.addAll({
          'id': proformaId.toString(), // ✅ FIXED
        });

      final streamedResponse = await request.send().timeout(
        const Duration(seconds: 60),
        onTimeout: () {
          throw TimeoutException('Request timed out after 60 seconds');
        },
      );

      final response = await http.Response.fromStream(streamedResponse);

      if (response.statusCode == 401 || response.statusCode == 403) {
        await _handleUnauthorized();
        throw Exception('Unauthorized - Token expired');
      }

      if (response.statusCode == 200) {
        debugPrint('✅ Proforma accepted successfully');
        return;
      }

      final decoded = _safeJsonDecode(response.body);
      if (response.statusCode == 422) {
        throw _formatValidationErrors(decoded);
      } else {
        final message =
            decoded?['message'] ?? decoded?['error'] ?? 'Something went wrong';
        throw {'error': message.toString()};
      }
    } on SocketException {
      throw {'error': 'No internet connection. Please check your network.'};
    } on TimeoutException {
      throw {'error': 'Request timed out. Please try again.'};
    } on FormatException {
      throw {'error': 'Invalid server response. Please try again.'};
    } catch (e) {
      if (e is Map<String, dynamic>) rethrow;
      debugPrint('❌ Proforma accept error: $e');
      throw {'error': 'Failed to accept proforma: ${e.toString()}'};
    }
  }
  
  // // 🔹 CHANGE PIN (Verify OTP)
  // Future<Map<String, dynamic>> changePin(String otp) async {
  //   try {
  //     final token = await _localStore.getToken();
  //     final uri = Uri.parse(ConstsApi.changePin).replace(
  //       queryParameters: {'otp': otp},
  //     );

  //     final response = await http.post(
  //       uri,
  //       headers: {
  //         'Authorization': 'Bearer $token',
  //         'Accept': 'application/json',
  //       },
  //     );

  //     final data = jsonDecode(response.body);
  //     debugPrint("🚀 Change Pin (Verify) API Response: $data");

  //     if (response.statusCode == 200 || response.statusCode == 201) {
  //       return {'success': true, 'data': data};
  //     } else {
  //       return {'success': false, 'error': _extractErrorMessage(data)};
  //     }
  //   } catch (e) {
  //     debugPrint('❌ Change Pin API Error: $e');
  //     return {
  //       'success': false,
  //       'error': 'Connection error. Please try again.',
  //     };
  //   }
  // }

  

  // 🔹 LOGOUT - ✅ Deletes FCM token
  Future<void> logout() async {
    try {
      final token = await _localStore.getToken();

      if (token == null || token.isEmpty) {
        debugPrint('⚠️ No token found — clearing data and deleting FCM token');
        await _messaging.deleteToken();
        await _localStore.clearAll(keepFcm: false);
        return;
      }

      final uri = Uri.parse(ConstsApi.logout);
      debugPrint('📡 Logging out from: $uri');

      final request = http.Request('POST', uri)
        ..headers.addAll({
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'Connection': 'keep-alive',
        });

      final streamedResponse = await request.send().timeout(
        const Duration(seconds: 30),
        onTimeout: () =>
            throw TimeoutException('Logout request timed out after 30 seconds'),
      );

      final response = await http.Response.fromStream(streamedResponse);

      debugPrint('📡 Logout Status Code: ${response.statusCode}');
      debugPrint('📦 Logout Body: ${response.body}');

      final decoded = _safeJsonDecode(response.body);

      if (response.statusCode == 200) {
        final message = decoded?['message'] ?? 'Logout successful';
        debugPrint('✅ $message');
      } else if (response.statusCode == 401) {
        debugPrint('⚠️ Token expired or unauthorized. Clearing storage.');
      } else {
        final message = decoded?['message'] ?? 'Logout failed';
        debugPrint('⚠️ $message');
      }
    } on SocketException {
      debugPrint('❌ No internet connection');
    } on TimeoutException {
      debugPrint('❌ Request timed out');
    } catch (e, stack) {
      debugPrint('❌ Logout error: $e');
      debugPrint('🧩 Stack trace: $stack');
    } finally {
      // ✅ ALWAYS delete FCM token and clear data on logout
      try {
        debugPrint('🗑️ Deleting FCM token on logout...');
        await _messaging.deleteToken();
        debugPrint('✅ FCM token deleted successfully');
      } catch (e) {
        debugPrint('⚠️ Error deleting FCM token: $e');
      }

      await _localStore.clearAll(keepFcm: false);
      debugPrint('✅ Local storage cleared');
    }
  }

  // Helper methods remain the same...
  Map<String, dynamic>? _safeJsonDecode(String body) {
    try {
      return jsonDecode(body) as Map<String, dynamic>?;
    } catch (e) {
      debugPrint('⚠️ JSON decode error: $e');
      return null;
    }
  }

  String _extractErrorMessage(
    dynamic data, [
    String defaultMsg = 'Operation failed',
  ]) {
    if (data is! Map) return defaultMsg;

    if (data['status'] == 'error') {
      if (data['errors'] is Map) {
        final errors = data['errors'] as Map;
        final firstErrorKey = errors.keys.first;
        final errorList = errors[firstErrorKey];

        if (errorList is List && errorList.isNotEmpty) {
          return errorList.first.toString();
        } else if (errorList is String) {
          return errorList;
        }
      } else if (data['errors'] is List &&
          (data['errors'] as List).isNotEmpty) {
        final firstError = (data['errors'] as List).first;
        if (firstError is String) {
          return firstError;
        } else if (firstError is Map && firstError['message'] != null) {
          return firstError['message'];
        }
      }
    }

    return data['message'] ??
        data['error'] ??
        data['msg'] ??
        data['detail'] ??
        defaultMsg;
  }

  // Additional helper methods (fetchProfile, fetchServiceHistory, etc.) remain unchanged
  // ... (include all other methods from your original code)
}

// 🔹 HELPER: Add file if exists
Future<void> _addFileIfExists(
  http.MultipartRequest request,
  File? file,
  String fieldName,
) async {
  if (file == null) return;

  try {
    if (!file.existsSync()) {
      debugPrint('⚠️ File not found: ${file.path}');
      return;
    }

    request.files.add(
      await http.MultipartFile.fromPath(
        fieldName,
        file.path,
        filename: basename(file.path),
      ),
    );
  } catch (e) {
    debugPrint('⚠️ Error adding file $fieldName: $e');
  }
}

// // 🔹 HELPER: Safe JSON decode
// Map<String, dynamic>? _safeJsonDecode(String body) {
//   try {
//     return jsonDecode(body) as Map<String, dynamic>?;
//   } catch (e) {
//     debugPrint('⚠️ JSON decode error: $e');
//     return null;
//   }
// }

// // 🔹 HELPER: Extract error message from API response
// String _extractErrorMessage(
//   dynamic data, [
//   String defaultMsg = 'Operation failed',
// ]) {
//   if (data is! Map) return defaultMsg;

//   // Check if status is error
//   if (data['status'] == 'error') {
//     // Handle errors object with field-specific errors
//     if (data['errors'] is Map) {
//       final errors = data['errors'] as Map;
//       final firstErrorKey = errors.keys.first;
//       final errorList = errors[firstErrorKey];

//       if (errorList is List && errorList.isNotEmpty) {
//         return errorList.first.toString();
//       } else if (errorList is String) {
//         return errorList;
//       }
//     }
//     // Handle errors as List
//     else if (data['errors'] is List && (data['errors'] as List).isNotEmpty) {
//       final firstError = (data['errors'] as List).first;
//       if (firstError is String) {
//         return firstError;
//       } else if (firstError is Map && firstError['message'] != null) {
//         return firstError['message'];
//       }
//     }
//   }

//   // Fallback to other common error formats
//   return data['message'] ??
//       data['error'] ??
//       data['msg'] ??
//       data['detail'] ??
//       defaultMsg;
// }

// 🔹 HELPER: Format validation errors
Map<String, String> _formatValidationErrors(Map<String, dynamic>? decoded) {
  if (decoded == null) return {'error': 'Validation failed'};

  final rawErrors =
      (decoded['errors'] ?? decoded['error'] ?? {}) as Map<String, dynamic>;

  final Map<String, String> formattedErrors = {};

  rawErrors.forEach((key, value) {
    if (value is List && value.isNotEmpty) {
      formattedErrors[key] = value.first.toString();
    } else if (value is String) {
      formattedErrors[key] = value;
    } else {
      formattedErrors[key] = 'Invalid input';
    }
  });

  return formattedErrors.isNotEmpty
      ? formattedErrors
      : {'error': 'Validation failed'};
}

// ⚡ Optimized HTTP Client for better performance
class OptimizedHttpClient {
  static final http.Client _client = http.Client();

  static http.Client get client => _client;

  static void dispose() {
    _client.close();
  }
}
