import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/legacy.dart';
import 'package:taxglide/controller/api_consts.dart';
import 'package:taxglide/controller/api_repository.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';

final apiRepositoryProvider = Provider((ref) => ApiRepository());

//login controller
final loginProvider =
    StateNotifierProvider<LoginNotifier, AsyncValue<Map<String, dynamic>>>(
      (ref) => LoginNotifier(ref.read(apiRepositoryProvider)),
    );

class LoginNotifier extends StateNotifier<AsyncValue<Map<String, dynamic>>> {
  final ApiRepository _apiRepository;

  LoginNotifier(this._apiRepository) : super(const AsyncValue.data({}));

  Future<void> loginWithPin(String email, String pin) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final result = await _apiRepository.loginWithPin(email, pin);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  Future<void> login(String mobile) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final model = LoginModel(mobile: mobile);
      final result = await _apiRepository.loginUser(model);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  Future<void> verifyOtp(String mobile, String otp) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final model = LoginModel(mobile: mobile, otp: otp);
      final result = await _apiRepository.verifyOtp(model);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }
}

//signup controller
final signupProvider =
    StateNotifierProvider<SignupNotifier, AsyncValue<Map<String, dynamic>>>(
      (ref) => SignupNotifier(ref.read(apiRepositoryProvider)),
    );

class SignupNotifier extends StateNotifier<AsyncValue<Map<String, dynamic>>> {
  final ApiRepository _apiRepository;

  SignupNotifier(this._apiRepository) : super(const AsyncValue.data({}));

  Future<void> signup(String name, String contactNumber, String email) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final model = SignupModel(
        name: name,
        contactNumber: contactNumber,
        email: email,
      );
      final result = await _apiRepository.signupUser(model);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  // Verify OTP for signup
  Future<void> verifySignupOtp(String email, String otp) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final result = await _apiRepository.verifySignupOtp(email, otp);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  // Reset state
  void reset() {
    state = const AsyncValue.data({});
  }
}

final employeeloginProvider =
    StateNotifierProvider<
      EmployeeLoginNotifier,
      AsyncValue<Map<String, dynamic>>
    >((ref) => EmployeeLoginNotifier(ref.read(apiRepositoryProvider)));

class EmployeeLoginNotifier
    extends StateNotifier<AsyncValue<Map<String, dynamic>>> {
  final ApiRepository _apiRepository;

  EmployeeLoginNotifier(this._apiRepository) : super(const AsyncValue.data({}));

  Future<void> loginWithPin(String email, String pin) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final result = await _apiRepository.employeeLoginWithPin(email, pin);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  Future<void> login(String mobile) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final result = await _apiRepository.employeeLogin(mobile);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  Future<void> verifyOtp(String mobile, String otp) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final model = LoginModel(mobile: mobile, otp: otp);
      final result = await _apiRepository.verifyOtp(model);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  void reset() {
    state = const AsyncValue.data({});
  }
}

final forgotPasswordProvider =
    StateNotifierProvider<
      ForgotPasswordNotifier,
      AsyncValue<Map<String, dynamic>>
    >((ref) => ForgotPasswordNotifier(ref.read(apiRepositoryProvider)));

class ForgotPasswordNotifier
    extends StateNotifier<AsyncValue<Map<String, dynamic>>> {
  final ApiRepository _apiRepository;

  ForgotPasswordNotifier(this._apiRepository)
    : super(const AsyncValue.data({}));

  Future<void> requestOtp(String email, bool isEmployee) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final result = await _apiRepository.requestForgotPasswordOtp(
        email,
        isEmployee,
      );
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  Future<void> verifyOtp(String email, String otp) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final result = await _apiRepository.verifyEmailOtp(email, otp);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }

  void reset() {
    state = const AsyncValue.data({});
  }
}


// PIN controller
final changePinProvider =
    StateNotifierProvider<PinNotifier, AsyncValue<Map<String, dynamic>>>(
  (ref) => PinNotifier(ref.read(apiRepositoryProvider)),
);

class PinNotifier extends StateNotifier<AsyncValue<Map<String, dynamic>>> {
  final ApiRepository _apiRepository;

  PinNotifier(this._apiRepository) : super(const AsyncValue.data({}));

  Future<void> changePin(String pinOrOtp) async {
    if (!mounted) return;
    state = const AsyncValue.loading();
    try {
      final result = await _apiRepository.changePin(pinOrOtp);
      if (mounted) state = AsyncValue.data(result);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }
}

final termsProvider = FutureProvider<TermsModel>((ref) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchTermsAndConditions();
});
final dashboardProvider = FutureProvider<DashBoardModel>((ref) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchDashboard();
});

final policyProvider = FutureProvider<TermsModel>((ref) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchpolicy();
});
final profileProvider = FutureProvider<ProfileGetModel>((ref) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchProfile();
});
final employeeProfileProvider = FutureProvider<EmployeeProfileModel>((
  ref,
) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchEmployeeProfile();
});

final serviceHistoryNotifierProvider =
    StateNotifierProvider.family<
      ServiceHistoryNotifier,
      AsyncValue<ServiceListHistoryModel>,
      String
    >((ref, type) {
      return ServiceHistoryNotifier(ref: ref, type: type);
    });

class ServiceHistoryNotifier
    extends StateNotifier<AsyncValue<ServiceListHistoryModel>> {
  final Ref ref;
  final String type;

  String? fromDate;
  String? toDate;

  ServiceHistoryNotifier({required this.ref, required this.type})
    : super(const AsyncValue.loading()) {
    fetchServiceHistory();
  }

  Future<void> fetchServiceHistory({bool isSilent = false}) async {
    try {
      if (!mounted) return;
      
      // 🔄 Skip loading state for silent refresh
      if (!isSilent) {
        state = const AsyncValue.loading();
      }

      final repo = ref.read(apiRepositoryProvider);

      final result = await repo.fetchServiceHistory(
        type,
        fromDate: fromDate,
        toDate: toDate,
      );

      if (!mounted) return;
      state = AsyncValue.data(result);
    } catch (e, st) {
      if (!mounted) return;
      state = AsyncValue.error(e, st);
    }
  }

  // ✅ APPLY FILTER
  void applyDateFilter({String? from, String? to}) {
    fromDate = from;
    toDate = to;
    fetchServiceHistory();
  }

  // ✅ CLEAR FILTER
  void clearFilter() {
    fromDate = null;
    toDate = null;
    fetchServiceHistory();
  }

  // ✅ MANUAL REFRESH
  Future<void> refresh() async {
    await fetchServiceHistory();
  }
}

final countProvider = FutureProvider.family<CountModel, String>((
  ref,
  type,
) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchCount(type);
});
final notificationTriggerProvider = StateProvider<int>((ref) => 0);

// 🔥 fetch notification count API
final notificationCountProvider =
    FutureProvider.autoDispose<NotificationCountModel>((ref) async {
      final repo = ref.read(apiRepositoryProvider);
      return await repo.fetchNotificationCount();
    });

final serviceDetailProvider = FutureProvider.family<DetailModel, int>((
  ref,
  id,
) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchServiceDetail(id);
});
final chatDocumentProvider = FutureProvider.family<ChatProfileModel, int>((
  ref,
  id,
) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchChatDocuments(id);
});

final serviceListProvider = FutureProvider<List<ServiceListModel>>((ref) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchServiceList();
});
final staffListProvider = FutureProvider<List<StaffModel>>((ref) async {
  final repo = ref.read(apiRepositoryProvider);
  return await repo.fetchStaffList();
});
final notificationProvider = StateNotifierProvider.family
    .autoDispose<
      NotificationNotifier,
      AsyncValue<List<NotificationModel>>,
      int
    >((ref, page) {
      return NotificationNotifier(ref: ref, page: page);
    });

// ---------------- NOTIFIER --------------------

class NotificationNotifier
    extends StateNotifier<AsyncValue<List<NotificationModel>>> {
  final Ref ref;
  final int page;

  NotificationNotifier({required this.ref, required this.page})
    : super(const AsyncValue.loading()) {
    fetchNotifications();
  }

  Future<void> fetchNotifications() async {
    if (!mounted) return;
    try {
      state = const AsyncValue.loading();

      final List<NotificationModel> notifications = await ApiRepository()
          .fetchNotificationList(page: page);

      if (mounted) state = AsyncValue.data(notifications);
    } catch (e, st) {
      if (mounted) state = AsyncValue.error(e, st);
    }
  }
}

final chatMessagesProvider =
    StateNotifierProvider.family<
      ChatMessagesNotifier,
      AsyncValue<List<MessageModel>>,
      int
    >((ref, chatId) {
      return ChatMessagesNotifier(ref: ref, chatId: chatId);
    });

class ChatMessagesNotifier
    extends StateNotifier<AsyncValue<List<MessageModel>>> {
  final Ref ref;
  final int chatId;

  int _currentPage = 1;
  bool _hasNextPage = true;
  bool _isFetching = false;
  bool _isInitialLoad = true;

  final List<MessageModel> _messages = [];

  ChatMessagesNotifier({required this.ref, required this.chatId})
    : super(const AsyncLoading()) {
    loadMessages();
  }

  /// ⭐ Getter to expose hasNextPage
  bool get hasNextPage => _hasNextPage;

  /// Load paginated messages (used for scroll pagination)
  Future<void> loadMessages() async {
    if (_isFetching || !_hasNextPage || !mounted) return;

    _isFetching = true;

    try {
      print("📥 Loading page $_currentPage for chat $chatId");

      final result = await ref
          .read(apiRepositoryProvider)
          .fetchChatMessages(chatId: chatId, page: _currentPage);

      if (result.isEmpty) {
        print("✅ No more messages - pagination complete");
        _hasNextPage = false;
      } else {
        print("✅ Loaded ${result.length} messages from page $_currentPage");

        if (_isInitialLoad) {
          // ⭐ FIX: On initial load, sort messages in ascending order (oldest first, newest last)
          result.sort((a, b) {
            final dateA =
                DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
            final dateB =
                DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
            return dateA.compareTo(dateB);
          });
          _messages.clear();
          _messages.addAll(result);
        } else {
          // ⭐ FIX: On pagination load, insert older messages at beginning
          // Make sure pagination result is sorted in ascending order
          result.sort((a, b) {
            final dateA =
                DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
            final dateB =
                DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
            return dateA.compareTo(dateB);
          });
          _messages.insertAll(0, result);
        }

        _currentPage++;
        if (mounted) state = AsyncData(List.from(_messages));
      }

      if (_isInitialLoad && result.isNotEmpty) {
        _isInitialLoad = false;
      }
    } catch (e, st) {
      print("❌ Error loading messages: $e");
      if (mounted) state = AsyncError(e, st);
    } finally {
      _isFetching = false;
    }
  }

  /// ⭐ Refresh chat (pull to refresh or after sending message)
  /// This is a SMOOTH refresh that doesn't show loading screen
  Future<void> refresh() async {
    print("🔄 Refreshing chat...");

    try {
      // ⭐ Get the latest messages from page 1
      final result = await ref
          .read(apiRepositoryProvider)
          .fetchChatMessages(chatId: chatId, page: 1);

      if (result.isNotEmpty) {
        // Sort in ascending order
        result.sort((a, b) {
          final dateA = DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
          final dateB = DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
          return dateA.compareTo(dateB);
        });

        // ⭐ Merge strategy: 
        // 1. Update existing messages (read/delivered status)
        // 2. Add new messages
        bool hasChanges = false;

        for (var incomingMsg in result) {
          final existingIndex = _messages.indexWhere((m) => m.id == incomingMsg.id);
          
          if (existingIndex != -1) {
            // Check if status changed
            final existingMsg = _messages[existingIndex];
            if (existingMsg.isRead != incomingMsg.isRead || 
                existingMsg.isDelivered != incomingMsg.isDelivered) {
              print("♻️ Updating status for message ID ${incomingMsg.id}");
              _messages[existingIndex] = incomingMsg;
              hasChanges = true;
            }
          } else {
            // ⭐ NEW: Check if this server message replaces an optimistic one
            final optimisticIndex = _messages.indexWhere(
              (m) => (m.id > 1000000000000 && 
                      m.message == incomingMsg.message && 
                      m.chatBy == incomingMsg.chatBy)
            );

            if (optimisticIndex != -1) {
              print("🔄 refresh: Replacing optimistic message with ID ${incomingMsg.id}");
              _messages[optimisticIndex] = incomingMsg;
              hasChanges = true;
            } else {
              // Truly new message (e.g. incoming from someone else)
              print("➕ Adding new message ID ${incomingMsg.id}");
              _messages.add(incomingMsg);
              hasChanges = true;
            }
          }
        }

        if (hasChanges) {
          // Sort the entire list to be sure
          _messages.sort((a, b) {
            final dateA = DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
            final dateB = DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
            return dateA.compareTo(dateB);
          });
          
          if (mounted) state = AsyncData(List.from(_messages));
        } else {
          print("✅ No new messages or status changes");
        }
      }
    } catch (e) {
      print("❌ Error refreshing: $e");
      // Don't throw error, just continue with existing messages
    }
  }

  /// ⭐ Hard refresh (for pull to refresh gesture)
  Future<void> hardRefresh() async {
    print("🔄 Hard refreshing chat...");
    _currentPage = 1;
    _hasNextPage = true;
    _messages.clear();
    _isInitialLoad = true;
    if (mounted) state = const AsyncLoading();
    await loadMessages();
  }

  /// Add new message to list (socket or send API)
  void addNewMessage(MessageModel msg) {
    print("➕ Adding new message: ${msg.message} (ID: ${msg.id})");

    // ⭐ DEDUPLICATION: Avoid adding the same message twice
    // 1. Check if ID already exists
    final existingIndex = _messages.indexWhere((m) => m.id == msg.id);
    if (existingIndex != -1) {
      print("♻️ Message ID ${msg.id} already exists, updating instead");
      _messages[existingIndex] = msg;
      if (mounted) state = AsyncData(List.from(_messages));
      return;
    }

    // 2. Check if this is a server confirmation of an optimistic message
    // Optimistic messages have large positive IDs from DateTime.now().millisecondsSinceEpoch
    final optimisticIndex = _messages.indexWhere(
      (m) =>
          (m.id > 1000000000000 &&
          m.message == msg.message &&
          m.chatBy == msg.chatBy),
    );

    if (optimisticIndex != -1) {
      print("🔄 Replacing optimistic message with server version");
      _messages[optimisticIndex] = msg;
    } else {
      // ⭐ Add at the END (newest messages at bottom)
      _messages.add(msg);
    }

    // Sort to ensure order (important if multiple local messages are sent)
    _messages.sort((a, b) {
            final dateA = DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
            final dateB = DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
            return dateA.compareTo(dateB);
          });

    if (mounted) state = AsyncData(List.from(_messages));
  }

  /// Update existing message (e.g., delivery status, read status)
  void updateMessage(MessageModel updatedMsg) {
    final index = _messages.indexWhere((m) => m.id == updatedMsg.id);
    if (index != -1) {
      _messages[index] = updatedMsg;
      if (mounted) state = AsyncData(List.from(_messages));
    }
  }

  /// Delete a message
  void deleteMessage(int messageId) {
    _messages.removeWhere((m) => m.id == messageId);
    if (mounted) state = AsyncData(List.from(_messages));
  }
}

// ✅ Provider for fetching both country and state data
final countryAndStatesProvider = FutureProvider<CountryModel>((ref) async {
  final repo = ref.read(apiRepositoryProvider);
  try {
    final model = await repo.fetchCountryAndStates(ConstsApi.conturystate);
    ref.keepAlive(); // Keep cached until manually disposed
    return model;
  } catch (e) {
    print('❌ Error in countryAndStatesProvider: $e');
    throw e;
  }
});
final fetchCityProvider = FutureProvider.family<CityModel, int>((
  ref,
  stateId,
) async {
  final repo = ref.read(apiRepositoryProvider);
  try {
    final cityModel = await repo.fetchCityByState(ConstsApi.city, stateId);
    ref.keepAlive();
    return cityModel;
  } catch (e) {
    print('❌ Error in fetchCityProvider: $e');
    throw e;
  }
});
