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/three_gang_bloc/three_gang_event.dart';
import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_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/group_three_gang_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/features/devices/model/three_gang_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';

class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
  final String threeGangId;
  final String switchCode;
  ThreeGangModel deviceStatus = ThreeGangModel(
      firstSwitch: false,
      secondSwitch: false,
      thirdSwitch: false,
      firstCountDown: 0,
      secondCountDown: 0,
      thirdCountDown: 0);
  Timer? _timer;

  bool threeGangGroup = false;
  List<DeviceModel> devicesList = [];
  List<GroupThreeGangModel> groupThreeGangList = [];
  bool allSwitchesOn = true;

  ThreeGangBloc({required this.threeGangId, required this.switchCode})
      : super(InitialState()) {
    on<InitialEvent>(_fetchThreeGangStatus);
    on<ThreeGangUpdated>(_threeGangUpdated);
    on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch);
    on<ChangeSecondSwitchStatusEvent>(_changeSecondSwitch);
    on<ChangeThirdSwitchStatusEvent>(_changeThirdSwitch);
    on<AllOffEvent>(_allOff);
    on<AllOnEvent>(_allOn);
    on<ChangeSlidingSegment>(_changeSliding);
    on<SetCounterValue>(_setCounterValue);
    on<GetCounterEvent>(_getCounterValue);
    on<TickTimer>(_onTickTimer);
    on<OnClose>(_onClose);
    on<GroupAllOnEvent>(_groupAllOn);
    on<GroupAllOffEvent>(_groupAllOff);

    on<ToggleDaySelectionEvent>(toggleDaySelection);
    on<ThreeGangSave>(saveSchedule);
    on<GetScheduleEvent>(getSchedule);
    on<ToggleScheduleEvent>(toggleChange);
    on<DeleteScheduleEvent>(deleteSchedule);

    on<ToggleSelectedEvent>(toggleSelectedIndex);
    on<ToggleCreateScheduleEvent>(toggleCreateSchedule);
  }

  void _fetchThreeGangStatus(
      InitialEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingInitialState());
    try {
      threeGangGroup = event.groupScreen;
      if (threeGangGroup) {
        devicesList = [];
        groupThreeGangList = [];
        allSwitchesOn = true;
        devicesList = await DevicesAPI.getDeviceByGroupName(
            HomeCubit.getInstance().selectedSpace?.id ?? '', '3G');

        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 = ThreeGangModel.fromJson(statusModelList);

          groupThreeGangList.add(GroupThreeGangModel(
              deviceId: devicesList[i].uuid ?? '',
              deviceName: devicesList[i].name ?? '',
              firstSwitch: deviceStatus.firstSwitch,
              secondSwitch: deviceStatus.secondSwitch,
              thirdSwitch: deviceStatus.thirdSwitch));
        }

        if (groupThreeGangList.isNotEmpty) {
          groupThreeGangList.firstWhere((element) {
            if (!element.firstSwitch ||
                !element.secondSwitch ||
                !element.thirdSwitch) {
              allSwitchesOn = false;
            }
            return true;
          });
        }
        emit(UpdateGroupState(
            threeGangList: groupThreeGangList, allSwitches: allSwitchesOn));
      } else {
        var response = await DevicesAPI.getDeviceStatus(threeGangId);
        List<StatusModel> statusModelList = [];
        for (var status in response['status']) {
          statusModelList.add(StatusModel.fromJson(status));
        }
        deviceStatus = ThreeGangModel.fromJson(statusModelList);
        emit(UpdateState(threeGangModel: deviceStatus));
        _listenToChanges();
      }
    } catch (e) {
      emit(FailedState(error: e.toString()));
      return;
    }
  }

  StreamSubscription<DatabaseEvent>? _streamSubscription;

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

      _streamSubscription = stream.listen((DatabaseEvent event) async {
        if (_timer != null) {
          await Future.delayed(const Duration(seconds: 1));
        }
        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 = ThreeGangModel.fromJson(statusList);
        if (!isClosed) {
          add(ThreeGangUpdated());
        }
      });
    } catch (_) {}
  }

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

  _threeGangUpdated(ThreeGangUpdated event, Emitter<ThreeGangState> emit) {
    emit(UpdateState(threeGangModel: deviceStatus));
  }

  void _changeFirstSwitch(
      ChangeFirstSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));
    try {
      if (threeGangGroup) {
        bool allSwitchesValue = true;
        groupThreeGangList.forEach((element) {
          if (element.deviceId == event.deviceId) {
            element.firstSwitch = !event.value;
          }
          if (!element.firstSwitch ||
              !element.secondSwitch ||
              !element.thirdSwitch) {
            allSwitchesValue = false;
          }
        });
        emit(UpdateGroupState(
            threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
      } else {
        deviceStatus.firstSwitch = !event.value;
        emit(UpdateState(threeGangModel: deviceStatus));
      }

      if (_timer != null) {
        _timer!.cancel();
      }

      _timer = Timer(const Duration(milliseconds: 100), () async {
        final response = await DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangGroup ? event.deviceId : threeGangId,
                code: 'switch_1',
                value: !event.value),
            threeGangGroup ? event.deviceId : threeGangId);

        if (!response['success']) {
          add(InitialEvent(groupScreen: threeGangGroup));
        }
      });
    } catch (_) {
      add(InitialEvent(groupScreen: threeGangGroup));
    }
  }

  void _changeSecondSwitch(
      ChangeSecondSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));
    try {
      if (threeGangGroup) {
        bool allSwitchesValue = true;
        groupThreeGangList.forEach((element) {
          if (element.deviceId == event.deviceId) {
            element.secondSwitch = !event.value;
          }
          if (!element.firstSwitch ||
              !element.secondSwitch ||
              !element.thirdSwitch) {
            allSwitchesValue = false;
          }
        });
        emit(UpdateGroupState(
            threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
      } else {
        deviceStatus.secondSwitch = !event.value;
        emit(UpdateState(threeGangModel: deviceStatus));
      }

      if (_timer != null) {
        _timer!.cancel();
      }
      _timer = Timer(const Duration(milliseconds: 100), () async {
        final response = await DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangGroup ? event.deviceId : threeGangId,
                code: 'switch_2',
                value: !event.value),
            threeGangGroup ? event.deviceId : threeGangId);

        if (!response['success']) {
          add(InitialEvent(groupScreen: threeGangGroup));
        }
      });
    } catch (_) {
      add(InitialEvent(groupScreen: threeGangGroup));
    }
  }

  void _changeThirdSwitch(
      ChangeThirdSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));
    try {
      if (threeGangGroup) {
        bool allSwitchesValue = true;
        groupThreeGangList.forEach((element) {
          if (element.deviceId == event.deviceId) {
            element.thirdSwitch = !event.value;
          }
          if (!element.firstSwitch ||
              !element.secondSwitch ||
              !element.thirdSwitch) {
            allSwitchesValue = false;
          }
        });
        emit(UpdateGroupState(
            threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
      } else {
        deviceStatus.thirdSwitch = !event.value;
        emit(UpdateState(threeGangModel: deviceStatus));
      }

      if (_timer != null) {
        _timer!.cancel();
      }

      _timer = Timer(const Duration(milliseconds: 100), () async {
        final response = await DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangGroup ? event.deviceId : threeGangId,
                code: 'switch_3',
                value: !event.value),
            threeGangGroup ? event.deviceId : threeGangId);

        if (!response['success']) {
          add(InitialEvent(groupScreen: threeGangGroup));
        }
      });
    } catch (_) {
      add(InitialEvent(groupScreen: threeGangGroup));
    }
  }

  void _allOff(AllOffEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));

    try {
      deviceStatus.firstSwitch = false;
      deviceStatus.secondSwitch = false;
      deviceStatus.thirdSwitch = false;
      emit(UpdateState(threeGangModel: deviceStatus));

      final response = await Future.wait([
        DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangId,
                code: 'switch_1',
                value: deviceStatus.firstSwitch),
            threeGangId),
        DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangId,
                code: 'switch_2',
                value: deviceStatus.secondSwitch),
            threeGangId),
        DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangId,
                code: 'switch_3',
                value: deviceStatus.thirdSwitch),
            threeGangId),
      ]);

      if (response.every((element) => !element['success'])) {
        await Future.delayed(const Duration(milliseconds: 500));
        add(const InitialEvent(groupScreen: false));
      }
    } catch (_) {
      await Future.delayed(const Duration(milliseconds: 500));
      add(const InitialEvent(groupScreen: false));
    }
  }

  void _allOn(AllOnEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));

    try {
      deviceStatus.firstSwitch = true;
      deviceStatus.secondSwitch = true;
      deviceStatus.thirdSwitch = true;
      emit(UpdateState(threeGangModel: deviceStatus));

      final response = await Future.wait([
        DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangId,
                code: 'switch_1',
                value: deviceStatus.firstSwitch),
            threeGangId),
        DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangId,
                code: 'switch_2',
                value: deviceStatus.secondSwitch),
            threeGangId),
        DevicesAPI.controlDevice(
            DeviceControlModel(
                deviceId: threeGangId,
                code: 'switch_3',
                value: deviceStatus.thirdSwitch),
            threeGangId),
      ]);

      if (response.every((element) => !element['success'])) {
        await Future.delayed(const Duration(milliseconds: 500));
        add(const InitialEvent(groupScreen: false));
      }
    } catch (_) {
      await Future.delayed(const Duration(milliseconds: 500));
      add(const InitialEvent(groupScreen: false));
    }
  }

  void _groupAllOn(GroupAllOnEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));
    try {
      for (int i = 0; i < groupThreeGangList.length; i++) {
        groupThreeGangList[i].firstSwitch = true;
        groupThreeGangList[i].secondSwitch = true;
        groupThreeGangList[i].thirdSwitch = true;
      }
      emit(UpdateGroupState(
          threeGangList: groupThreeGangList, allSwitches: true));

      List<String> allDeviceIds =
          groupThreeGangList.map((device) => device.deviceId).toList();

      final response1 = await DevicesAPI.deviceBatchController(
        code: 'switch_1',
        devicesUuid: allDeviceIds,
        value: true,
      );
      final response2 = await DevicesAPI.deviceBatchController(
        code: 'switch_2',
        devicesUuid: allDeviceIds,
        value: true,
      );
      final response3 = await DevicesAPI.deviceBatchController(
        code: 'switch_3',
        devicesUuid: allDeviceIds,
        value: true,
      );

      if (response1['failedResults'].toString() != '[]' ||
          response2['failedResults'].toString() != '[]' ||
          response3['failedResults'].toString() != '[]') {
        await Future.delayed(const Duration(milliseconds: 500));
        add(const InitialEvent(groupScreen: true));
      }
    } catch (_) {
      await Future.delayed(const Duration(milliseconds: 500));
      add(const InitialEvent(groupScreen: true));
    }
  }

  void _groupAllOff(
      GroupAllOffEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));
    try {
      for (int i = 0; i < groupThreeGangList.length; i++) {
        groupThreeGangList[i].firstSwitch = false;
        groupThreeGangList[i].secondSwitch = false;
        groupThreeGangList[i].thirdSwitch = false;
      }
      emit(UpdateGroupState(
          threeGangList: groupThreeGangList, allSwitches: false));
      List<String> allDeviceIds =
          groupThreeGangList.map((device) => device.deviceId).toList();

      final response1 = await DevicesAPI.deviceBatchController(
        code: 'switch_1',
        devicesUuid: allDeviceIds,
        value: false,
      );
      final response2 = await DevicesAPI.deviceBatchController(
        code: 'switch_2',
        devicesUuid: allDeviceIds,
        value: false,
      );
      final response3 = await DevicesAPI.deviceBatchController(
        code: 'switch_3',
        devicesUuid: allDeviceIds,
        value: false,
      );

      if (response1['failedResults'].toString() != '[]' ||
          response2['failedResults'].toString() != '[]' ||
          response3['failedResults'].toString() != '[]') {
        await Future.delayed(const Duration(milliseconds: 500));
        add(const InitialEvent(groupScreen: true));
      }
    } catch (_) {
      await Future.delayed(const Duration(milliseconds: 500));
      add(const InitialEvent(groupScreen: true));
    }
  }

  void _changeSliding(
      ChangeSlidingSegment event, Emitter<ThreeGangState> emit) async {
    emit(ChangeSlidingSegmentState(value: event.value));
  }

  void _setCounterValue(
      SetCounterValue event, Emitter<ThreeGangState> emit) async {
    emit(LoadingNewSate(threeGangModel: deviceStatus));
    int seconds = 0;
    try {
      seconds = event.duration.inSeconds;
      final response = await DevicesAPI.controlDevice(
          DeviceControlModel(
              deviceId: threeGangId, code: event.deviceCode, value: seconds),
          threeGangId);

      if (response['success'] ?? false) {
        if (event.deviceCode == 'countdown_1') {
          deviceStatus.firstCountDown = seconds;
        } else if (event.deviceCode == 'countdown_2') {
          deviceStatus.secondCountDown = seconds;
        } else if (event.deviceCode == 'countdown_3') {
          deviceStatus.thirdCountDown = seconds;
        }
      } else {
        emit(const FailedState(error: 'Something went wrong'));
        return;
      }
    } catch (e) {
      emit(FailedState(error: e.toString()));
      return;
    }
    if (seconds > 0) {
      _onStartTimer(seconds);
    } else {
      _timer?.cancel();
      emit(TimerRunComplete());
    }
  }

  void _getCounterValue(
      GetCounterEvent event, Emitter<ThreeGangState> emit) async {
    emit(LoadingInitialState());
    try {
      var response = await DevicesAPI.getDeviceStatus(threeGangId);
      List<StatusModel> statusModelList = [];
      for (var status in response['status']) {
        statusModelList.add(StatusModel.fromJson(status));
      }
      deviceStatus = ThreeGangModel.fromJson(statusModelList);

      if (event.deviceCode == 'countdown_1') {
        deviceStatus.firstCountDown > 0
            ? _onStartTimer(deviceStatus.firstCountDown)
            : emit(UpdateTimerState(seconds: deviceStatus.firstCountDown));
      } else if (event.deviceCode == 'countdown_2') {
        deviceStatus.secondCountDown > 0
            ? _onStartTimer(deviceStatus.secondCountDown)
            : emit(UpdateTimerState(seconds: deviceStatus.secondCountDown));
      } else if (event.deviceCode == 'countdown_3') {
        deviceStatus.thirdCountDown > 0
            ? _onStartTimer(deviceStatus.thirdCountDown)
            : emit(UpdateTimerState(seconds: deviceStatus.thirdCountDown));
      }
    } catch (e) {
      emit(FailedState(error: e.toString()));
      return;
    }
  }

  void _onClose(OnClose event, Emitter<ThreeGangState> emit) {
    _timer?.cancel();
  }

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

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

  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<ThreeGangState> emit,
  ) async {
    emit(LoadingInitialState());
    if (selectedDays.contains(event.key)) {
      selectedDays.remove(event.key);
    } else {
      selectedDays.add(event.key);
    }
    emit(ChangeTimeState());
    add(ChangeSlidingSegment(value: 1));
  }

  Future<void> saveSchedule(
    ThreeGangSave event,
    Emitter<ThreeGangState> emit,
  ) async {
    try {
      if (selectedDays.isNotEmpty) {
        emit(LoadingInitialState());
        await DevicesAPI.postSchedule(
            category: switchCode,
            deviceId: threeGangId,
            time: getTimeStampWithoutSeconds(selectedTime).toString(),
            code: switchCode,
            value: toggleSchedule,
            days: selectedDays);
        CustomSnackBar.displaySnackBar('Save Successfully');
        add(GetScheduleEvent());
        emit(ThreeGangSaveSchedule());
        add(const ToggleCreateScheduleEvent(index: 1));
      } else {
        CustomSnackBar.displaySnackBar('Please select days');
      }
    } catch (e) {
      emit(FailedState(error: e.toString()));
    }
  }

  Future<void> getSchedule(
    GetScheduleEvent event,
    Emitter<ThreeGangState> emit,
  ) async {
    try {
      emit(LoadingInitialState());
      final response = await DevicesAPI.getSchedule(
        category: switchCode,
        deviceId: threeGangId,
      );
      List<dynamic> jsonData = response;
      listSchedule =
          jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
      emit(InitialState());
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(FailedState(error: 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<ThreeGangState> emit) async {
    try {
      emit(LoadingInitialState());
      final response = await DevicesAPI.changeSchedule(
          scheduleId: event.id, deviceUuid: threeGangId, enable: event.toggle);
      if (response == true) {
        add(GetScheduleEvent());
        toggleSchedule = event.toggle;
        return toggleSchedule;
      }
      emit(IsToggleState(onOff: toggleSchedule));
      add(const ChangeSlidingSegment(value: 1));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(FailedState(error: errorMessage.toString()));
    }
  }

  Future deleteSchedule(
      DeleteScheduleEvent event, Emitter<ThreeGangState> emit) async {
    try {
      emit(LoadingInitialState());
      final response = await DevicesAPI.deleteSchedule(
        scheduleId: event.id,
        deviceUuid: threeGangId,
      );
      if (response == true) {
        add(GetScheduleEvent());
        return toggleSchedule;
      }
      emit(IsToggleState(onOff: toggleSchedule));
      add(const ChangeSlidingSegment(value: 1));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(FailedState(error: errorMessage.toString()));
    }
  }

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

  void toggleCreateSchedule(
      ToggleCreateScheduleEvent event, Emitter<ThreeGangState> emit) {
    emit(LoadingInitialState());
    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();
}
