import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/garage_door_model.dart';
import 'package:syncrow_app/features/devices/model/group_garage_model.dart';
import 'package:syncrow_app/features/devices/model/schedule_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';

class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
  final String GDId;
  GarageDoorBloc({
    required this.GDId,
  }) : super(const GarageDoorSensorState()) {
    on<GarageDoorInitial>(_fetchStatus);
    on<ReportLogsInitial>(fetchLogsForLastMonth);
    on<ToggleClosingReminderEvent>(_toggleClosingReminder);
    on<ToggleDoorAlarmEvent>(_toggleDoorAlarm);
    on<ToggleDaySelectionEvent>(toggleDaySelection);
    on<ScheduleSaveapp>(saveSchedule);
    on<GetScheduleEvent>(getSchedule);
    on<ToggleScheduleEvent>(toggleChange);
    on<ToggleCreateScheduleEvent>(toggleCreateSchedule);
    on<OnClose>(_onClose);
    on<SelectSecondsEvent>(selectSeconds);
    on<ToggleDoorEvent>(openCloseGarageDoor);
    on<GetCounterEvent>(_getCounterValue);
    on<SetCounterValue>(_setCounterValue);
    on<SetTimeOutValue>(_setTimeOutAlarm);
    on<TickTimer>(_onTickTimer);
    on<InitialWizardEvent>(_fetchWizardStatus);
    on<GroupAllOnEvent>(_groupAllOn);
    on<GroupAllOffEvent>(_groupAllOff);
    on<ChangeFirstWizardSwitchStatusEvent>(_changeFirstWizardSwitch);
    on<ToggleAlarmEvent>(_toggleAlarmEvent);
    on<DeleteScheduleEvent>(deleteSchedule);

    on<UpdateStateEvent>(_updateState);
  }
  void _onClose(OnClose event, Emitter<GarageDoorSensorState> emit) {
    _timer?.cancel();
  }

  Timer? _timer;
  bool lowBattery = false;
  bool closingReminder = false;
  bool doorAlarm = false;

  GarageDoorModel deviceStatus = GarageDoorModel(
    tr_timecon: 0,
    countdown1: 0,
    countdownAlarm: 0,
    doorContactState: false,
    doorControl1: '',
    doorState1: '',
    switch1: false,
    voiceControl1: false,
    batteryPercentage: 0,
  );

  void _fetchStatus(
      GarageDoorInitial event, Emitter<GarageDoorSensorState> emit) async {
    emit(GarageDoorLoadingState());
    try {
      var response = await DevicesAPI.getDeviceStatus(GDId);
      List<StatusModel> statusModelList = [];
      for (var status in response['status']) {
        statusModelList.add(StatusModel.fromJson(status));
      }
      deviceStatus = GarageDoorModel.fromJson(
        statusModelList,
      );
      secondSelected = deviceStatus.tr_timecon;
      toggleDoor = deviceStatus.switch1;
      emit(UpdateState(garageSensor: deviceStatus));
      Future.delayed(const Duration(milliseconds: 500));
      _listenToChanges();
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
      return;
    }
  }

  void _toggleAlarmEvent(
    ToggleAlarmEvent event,
    Emitter<GarageDoorSensorState> emit,
  ) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));

    try {
      deviceStatus.doorState1 = event.isEnabled;

      emit(UpdateState(garageSensor: deviceStatus));

      await DevicesAPI.controlDevice(
        DeviceControlModel(
          deviceId: GDId,
          code: 'door_state_1',
          value: event.isEnabled,
        ),
        GDId,
      );

      emit(UpdateState(garageSensor: deviceStatus));
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
    }
  }

  void _toggleClosingReminder(ToggleClosingReminderEvent event,
      Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));
    try {
      closingReminder = event.isClosingReminderEnabled;
      emit(UpdateState(garageSensor: deviceStatus));
      await DevicesAPI.controlDevice(
        DeviceControlModel(
          deviceId: GDId,
          code: 'closing_reminder',
          value: closingReminder,
        ),
        GDId,
      );
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
    }
  }

  void _toggleDoorAlarm(
      ToggleDoorAlarmEvent event, Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));
    try {
      doorAlarm = event.isDoorAlarmEnabled;
      emit(UpdateState(garageSensor: deviceStatus));
      await DevicesAPI.controlDevice(
        DeviceControlModel(
          deviceId: GDId,
          code: 'door_alarm',
          value: doorAlarm,
        ),
        GDId,
      );
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
    }
  }

  DeviceReport recordGroups =
      DeviceReport(startTime: '0', endTime: '0', data: []);

  Future<void> fetchLogsForLastMonth(
      ReportLogsInitial event, Emitter<GarageDoorSensorState> emit) async {
    DateTime now = DateTime.now();

    DateTime lastMonth = DateTime(now.year, now.month - 1, now.day);
    int startTime = lastMonth.millisecondsSinceEpoch;
    int endTime = now.millisecondsSinceEpoch;
    try {
      emit(GarageDoorLoadingState());
      var response = await DevicesAPI.getReportLogs(
        startTime: startTime.toString(),
        endTime: endTime.toString(),
        deviceUuid: GDId,
        code: 'switch_1',
      );
      recordGroups = response;

      emit(UpdateState(garageSensor: deviceStatus));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(GarageDoorFailedState(errorMessage: errorMessage));
    }
  }

  // Real-time db
  StreamSubscription<DatabaseEvent>? _streamSubscription;
  void _listenToChanges() {
    try {
      _streamSubscription?.cancel();
      DatabaseReference ref =
          FirebaseDatabase.instance.ref('device-status/$GDId');
      Stream<DatabaseEvent> stream = ref.onValue;

      _streamSubscription = stream.listen((DatabaseEvent event) async {
        if (_timer != null) {
          await Future.delayed(const Duration(seconds: 1));
        }

        if (event.snapshot.value != null) {
          Map<dynamic, dynamic> usersMap =
              event.snapshot.value as Map<dynamic, dynamic>;
          List<StatusModel> statusList = [];

          usersMap['status'].forEach((element) {
            statusList.add(
                StatusModel(code: element['code'], value: element['value']));
          });
          deviceStatus.tr_timecon = statusList
              .firstWhere((status) => status.code == "tr_timecon",
                  orElse: () => StatusModel(code: "tr_timecon", value: 0))
              .value as int;

          deviceStatus.switch1 = statusList
              .firstWhere((status) => status.code == "switch_1",
                  orElse: () => StatusModel(code: "switch_1", value: false))
              .value as bool;
          secondSelected = deviceStatus.tr_timecon;
          toggleDoor = deviceStatus.switch1;
          add(UpdateStateEvent());
        }
      });
    } catch (_) {}
  }

  Future<void> _updateState(
      UpdateStateEvent event, Emitter<GarageDoorSensorState> emit) async {
    emit(GarageDoorLoadingState());
    emit(UpdateState(garageSensor: deviceStatus));
  }

  @override
  Future<void> close() async {
    _streamSubscription?.cancel();
    _streamSubscription = null;
    return super.close();
  }

  List<Map<String, String>> days = [
    {"day": "Sun", "key": "Sun"},
    {"day": "Mon", "key": "Mon"},
    {"day": "Tue", "key": "Tue"},
    {"day": "Wed", "key": "Wed"},
    {"day": "Thu", "key": "Thu"},
    {"day": "Fri", "key": "Fri"},
    {"day": "Sat", "key": "Sat"},
  ];

  Future<void> toggleDaySelection(
    ToggleDaySelectionEvent event,
    Emitter<GarageDoorSensorState> emit,
  ) async {
    emit(GarageDoorLoadingState());
    if (selectedDays.contains(event.key)) {
      selectedDays.remove(event.key);
    } else {
      selectedDays.add(event.key);
    }
    emit(ChangeTimeState());
  }

  Future<void> saveSchedule(
    ScheduleSaveapp event,
    Emitter<GarageDoorSensorState> emit,
  ) async {
    try {
      if (selectedDays.isNotEmpty) {
        emit(GarageDoorLoadingState());
        await DevicesAPI.postSchedule(
            category: 'switch_1',
            deviceId: GDId,
            time: getTimeStampWithoutSeconds(selectedTime).toString(),
            code: 'switch_1',
            value: toggleSchedule,
            days: selectedDays);
        CustomSnackBar.displaySnackBar('Save Successfully');
        add(GetScheduleEvent());
        add(const ToggleCreateScheduleEvent(index: 1));
      } else {
        CustomSnackBar.displaySnackBar('Please select days');
      }
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
    }
  }

  Future<void> getSchedule(
    GetScheduleEvent event,
    Emitter<GarageDoorSensorState> emit,
  ) async {
    try {
      emit(GarageDoorLoadingState());
      final response = await DevicesAPI.getSchedule(
        category: 'switch_1',
        deviceId: GDId,
      );
      List<dynamic> jsonData = response;
      listSchedule =
          jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
      emit(UpdateState(garageSensor: deviceStatus));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(GarageDoorFailedState(errorMessage: errorMessage.toString()));
    }
  }

  int? getTimeStampWithoutSeconds(DateTime? dateTime) {
    if (dateTime == null) return null;
    DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
        dateTime.day, dateTime.hour, dateTime.minute);
    return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
  }

  Future toggleChange(
      ToggleScheduleEvent event, Emitter<GarageDoorSensorState> emit) async {
    try {
      emit(GarageDoorLoadingState());
      final response = await DevicesAPI.changeSchedule(
          scheduleId: event.id, deviceUuid: GDId, enable: event.toggle);
      if (response == true) {
        add(GetScheduleEvent());
        toggleSchedule = event.toggle;
        return toggleSchedule;
      }
      emit(IsToggleState(onOff: toggleSchedule));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(GarageDoorFailedState(errorMessage: errorMessage.toString()));
    }
  }

  Future deleteSchedule(
      DeleteScheduleEvent event, Emitter<GarageDoorSensorState> emit) async {
    try {
      emit(GarageDoorLoadingState());
      final response = await DevicesAPI.deleteSchedule(
        scheduleId: event.id,
        deviceUuid: GDId,
      );
      if (response == true) {
        add(GetScheduleEvent());
        return toggleSchedule;
      }
      emit(IsToggleState(onOff: toggleSchedule));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(GarageDoorFailedState(errorMessage: errorMessage.toString()));
    }
  }

  void toggleSelectedIndex(
      ToggleSelectedEvent event, Emitter<GarageDoorSensorState> emit) {
    emit(GarageDoorLoadingState());
    selectedTabIndex = event.index;
    emit(ChangeSlidingSegmentState(value: selectedTabIndex));
  }

  void toggleCreateSchedule(
      ToggleCreateScheduleEvent event, Emitter<GarageDoorSensorState> emit) {
    emit(GarageDoorLoadingState());
    createSchedule = !createSchedule;
    selectedDays.clear();
    selectedTime = DateTime.now();
    emit(UpdateCreateScheduleState(createSchedule));
  }

  int selectedTabIndex = 0;
  bool toggleSchedule = true;
  List<String> selectedDays = [];
  bool createSchedule = false;
  List<ScheduleModel> listSchedule = [];
  DateTime? selectedTime = DateTime.now();

  int secondSelected = 0;
  bool toggleDoor = false;
  Future<void> selectSeconds(
      SelectSecondsEvent event, Emitter<GarageDoorSensorState> emit) async {
    try {
      emit(GarageDoorLoadingState());
      secondSelected = event.seconds;

      await DevicesAPI.controlDevice(
          DeviceControlModel(
              deviceId: GDId, code: 'tr_timecon', value: secondSelected),
          GDId);
      emit(UpdateState(garageSensor: deviceStatus));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(GarageDoorFailedState(errorMessage: errorMessage.toString()));
    }
  }

  openCloseGarageDoor(
      ToggleDoorEvent event, Emitter<GarageDoorSensorState> emit) async {
    emit(GarageDoorLoadingState());
    try {
      toggleDoor = !event.toggle;
      await DevicesAPI.controlDevice(
          DeviceControlModel(
              deviceId: GDId, code: 'switch_1', value: toggleDoor),
          GDId);
      add(const GarageDoorInitial());
      emit(UpdateState(garageSensor: deviceStatus));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(GarageDoorFailedState(errorMessage: errorMessage.toString()));
    }
  }

  void _setCounterValue(
      SetCounterValue event, Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));
    int seconds = 0;
    try {
      seconds = event.duration.inSeconds;
      final response = await DevicesAPI.controlDevice(
          DeviceControlModel(
              deviceId: GDId, code: 'countdown_1', value: seconds),
          GDId);

      if (response['success'] ?? false) {
        deviceStatus.countdown1 = seconds;
      } else {
        emit(const GarageDoorFailedState(errorMessage: 'Something went wrong'));
        return;
      }
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
      return;
    }
    if (seconds > 0) {
      _onStartTimer(seconds);
    } else {
      _timer?.cancel();
      emit(TimerRunComplete());
    }
  }

  void _getCounterValue(
      GetCounterEvent event, Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingInitialState());
    try {
      var response = await DevicesAPI.getDeviceStatus(GDId);
      List<StatusModel> statusModelList = [];
      for (var status in response['status']) {
        statusModelList.add(StatusModel.fromJson(status));
      }
      deviceStatus = GarageDoorModel.fromJson(statusModelList);
      if (event.deviceCode == 'countdown_1') {
        deviceStatus.countdown1 > 0
            ? _onStartTimer(deviceStatus.countdown1)
            : emit(UpdateTimerState(seconds: deviceStatus.countdown1));
      }
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
      return;
    }
  }

  void _onStartTimer(int seconds) {
    _timer?.cancel();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      add(TickTimer(remainingTime: seconds - timer.tick));
    });
  }

  void _onTickTimer(TickTimer event, Emitter<GarageDoorSensorState> emit) {
    if (event.remainingTime > 0) {
      emit(TimerRunInProgress(event.remainingTime));
    } else {
      _timer?.cancel();
      emit(TimerRunComplete());
    }
  }

  bool oneGangGroup = false;
  List<DeviceModel> devicesList = [];
  List<GroupGarageModel> groupList = [];
  bool allSwitchesOn = true;

  void _fetchWizardStatus(
      InitialWizardEvent event, Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingInitialState());
    try {
      devicesList = [];
      groupList = [];
      allSwitchesOn = true;
      devicesList = await DevicesAPI.getDeviceByGroupName(
          HomeCubit.getInstance().selectedSpace?.id ?? '', 'GD');

      for (int i = 0; i < devicesList.length; i++) {
        var response =
            await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
        List<StatusModel> statusModelList = [];
        for (var status in response['status']) {
          statusModelList.add(StatusModel.fromJson(status));
        }
        deviceStatus = GarageDoorModel.fromJson(statusModelList);
        groupList.add(GroupGarageModel(
          deviceId: devicesList[i].uuid ?? '',
          deviceName: devicesList[i].name ?? '',
          firstSwitch: deviceStatus.switch1,
        ));
      }

      if (groupList.isNotEmpty) {
        groupList.firstWhere((element) {
          if (!element.firstSwitch) {
            allSwitchesOn = false;
          }
          return true;
        });
      }
      emit(UpdateGroupState(garageList: groupList, allSwitches: allSwitchesOn));
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));

      return;
    }
  }

  void _groupAllOn(
      GroupAllOnEvent event, Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));
    try {
      // Set all switches (firstSwitch and secondSwitch) based on the event value (on/off)
      for (int i = 0; i < groupList.length; i++) {
        groupList[i].firstSwitch = true;
      }

      // Emit the state with updated values
      emit(UpdateGroupState(garageList: groupList, allSwitches: true));

      // Get a list of all device IDs
      List<String> allDeviceIds =
          groupList.map((device) => device.deviceId).toList();

      // First call for switch_1
      final response = await DevicesAPI.deviceBatchController(
        code: 'switch_1', // Controls first switch for all devices
        devicesUuid: allDeviceIds,
        value: true, // true (on) or false (off) depending on the event value
      );
      // Check if either response is unsuccessful, then reset to initial state
      if (response['failedResults'].toString() != '[]') {
        await Future.delayed(const Duration(milliseconds: 500));
        add(const InitialEvent(groupScreen: true));
      }
    } catch (_) {
      // In case of an error, delay and reset the screen to initial state
      await Future.delayed(const Duration(milliseconds: 500));
      add(const InitialEvent(groupScreen: true));
    }
  }

  void _groupAllOff(
      GroupAllOffEvent event, Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));
    try {
      // Set all switches (firstSwitch and secondSwitch) based on the event value (on/off)
      for (int i = 0; i < groupList.length; i++) {
        groupList[i].firstSwitch = false;
      }

      // Emit the state with updated values
      emit(UpdateGroupState(garageList: groupList, allSwitches: false));

      // Get a list of all device IDs
      List<String> allDeviceIds =
          groupList.map((device) => device.deviceId).toList();

      // First call for switch_1
      final response = await DevicesAPI.deviceBatchController(
        code: 'switch_1', // Controls first switch for all devices
        devicesUuid: allDeviceIds,
        value: false, // true (on) or false (off) depending on the event value
      );

      // Check if either response is unsuccessful, then reset to initial state
      if (response['failedResults'].toString() != '[]') {
        await Future.delayed(const Duration(milliseconds: 500));
        add(const InitialEvent(groupScreen: true));
      }
    } catch (_) {
      // In case of an error, delay and reset the screen to initial state
      await Future.delayed(const Duration(milliseconds: 500));
      add(const InitialEvent(groupScreen: true));
    }
  }

  void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event,
      Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));
    try {
      bool allSwitchesValue = true;
      groupList.forEach((element) {
        if (element.deviceId == event.deviceId) {
          element.firstSwitch = !event.value;
        }
        if (!element.firstSwitch) {
          allSwitchesValue = false;
        }
      });

      emit(UpdateGroupState(
          garageList: groupList, allSwitches: allSwitchesValue));

      final response = await DevicesAPI.deviceBatchController(
        code: 'switch_1',
        devicesUuid: [event.deviceId],
        value: !event.value,
      );

      if (response['failedResults'].toString() != '[]') {
        add(InitialEvent(groupScreen: oneGangGroup));
      }
    } catch (_) {
      add(InitialEvent(groupScreen: oneGangGroup));
    }
  }

  void _setTimeOutAlarm(
      SetTimeOutValue event, Emitter<GarageDoorSensorState> emit) async {
    emit(LoadingNewSate(doorSensor: deviceStatus));
    int seconds = 0;
    try {
      seconds = event.duration.inSeconds;
      final response = await DevicesAPI.controlDevice(
          DeviceControlModel(
              deviceId: GDId, code: 'countdown_alarm', value: seconds),
          GDId);

      if (response['success'] ?? false) {
        deviceStatus.countdownAlarm = seconds;
        CustomSnackBar.displaySnackBar('Save Successfully');
        add(GetScheduleEvent());
      } else {
        emit(const GarageDoorFailedState(errorMessage: 'Something went wrong'));
        return;
      }
    } catch (e) {
      emit(GarageDoorFailedState(errorMessage: e.toString()));
      return;
    }
  }
}
