import 'dart:async';
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/acs_bloc/acs_event.dart';
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_state.dart';
import 'package:syncrow_app/features/devices/model/ac_model.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/status_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';

class ACsBloc extends Bloc<AcsEvent, AcsState> {
  final String acId;
  AcStatusModel deviceStatus = AcStatusModel(
      countdown1: 0,
      uuid: '',
      acSwitch: true,
      modeString: 'hot',
      tempSet: 300,
      currentTemp: 315,
      fanSpeedsString: 'low',
      childLock: false);
  List<AcStatusModel> deviceStatusList = [];
  List<DeviceModel> devicesList = [];
  bool allAcsPage = false;
  bool allAcsOn = true;
  bool allTempSame = true;
  int globalTemp = 250;
  Timer? _timer;

  ACsBloc({required this.acId}) : super(AcsInitialState()) {
    on<AcsInitial>(_fetchAcsStatus);
    on<AcSwitch>(_changeAcSwitch);
    on<IncreaseCoolToTemp>(_increaseCoolTo);
    on<DecreaseCoolToTemp>(_decreaseCoolTo);
    on<ChangeLock>(_changeLockValue);
    on<ChangeAcMode>(_changeAcMode);
    on<ChangeFanSpeed>(_changeFanSpeed);
    on<ChangeAllSwitch>(_changeAllAcSwitch);
    on<IncreaseAllTemp>(_increaseAllTemp);
    on<DecreaseAllTemp>(_decreaseAllTemp);
    on<AcUpdated>(_onAcUpdated);
    on<OnClose>(_onClose);
    on<GetCounterEvent>(_getCounterValue);
    on<SetCounterValue>(_setCounterValue);
    on<TickTimer>(_onTickTimer);

    // on<SetTimeOutValue>(_setTimeOutAlarm);
  }

  void _fetchAcsStatus(AcsInitial event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    try {
      allAcsPage = event.allAcs;
      if (event.allAcs) {
        await _getAllAcs();
        emit(GetAllAcsStatusState(
            allAcsStatues: deviceStatusList,
            allAcs: devicesList,
            allOn: allAcsOn,
            allTempSame: allTempSame,
            temp: globalTemp));
      } else {
        var response = await DevicesAPI.getDeviceStatus(acId);
        List<StatusModel> statusModelList = [];
        for (var status in response['status']) {
          statusModelList.add(StatusModel.fromJson(status));
        }
        deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList);
        emit(GetAcStatusState(acStatusModel: deviceStatus));
        Future.delayed(const Duration(milliseconds: 500));
        _listenToChanges(acId);
      }
    } catch (e) {
      emit(AcsFailedState(errorMessage: e.toString()));
      return;
    }
  }

  StreamSubscription<DatabaseEvent>? _streamSubscription;

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

      _streamSubscription = stream.listen((DatabaseEvent event) {
        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 =
            AcStatusModel.fromJson(usersMap['productUuid'], statusList);

        add(AcUpdated());
      });
    } catch (_) {}
  }

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

  _onAcUpdated(AcUpdated event, Emitter<AcsState> emit) {
    emit(GetAcStatusState(acStatusModel: deviceStatus));
  }

  _getAllAcs() async {
    deviceStatusList = [];
    devicesList = [];
    devicesList = await DevicesAPI.getDeviceByGroupName(
        HomeCubit.getInstance().selectedSpace?.id ?? '', 'AC');

    for (int i = 0; i < devicesList.length; i++) {
      _listenToChanges(devicesList[i].uuid);
      var response =
          await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
      List<StatusModel> statusModelList = [];
      for (var status in response['status']) {
        statusModelList.add(StatusModel.fromJson(status));
      }
      deviceStatusList.add(AcStatusModel.fromJson(devicesList[i].uuid ?? '', statusModelList));
    }

    _setAllAcsTempsAndSwitches();
  }

  void _changeAcSwitch(AcSwitch event, Emitter<AcsState> emit) async {
    final acSwitchValue = !event.acSwitch;
    if (allAcsPage) {
      emit(AcsLoadingState());
      for (AcStatusModel ac in deviceStatusList) {
        if (ac.uuid == event.deviceId) {
          ac.acSwitch = acSwitchValue;
        }
      }

      _setAllAcsTempsAndSwitches();
      _emitAcsStatus(emit);
    } else {
      emit(AcChangeLoading(acStatusModel: deviceStatus));
      deviceStatus.acSwitch = acSwitchValue;
      emit(AcModifyingState(acStatusModel: deviceStatus));
    }

    await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'switch', value: acSwitchValue);
  }

  void _changeAllAcSwitch(ChangeAllSwitch event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    if (deviceStatusList.length == devicesList.length) {
      for (int i = 0; i < deviceStatusList.length; i++) {
        deviceStatusList[i].acSwitch = event.value;
      }
    }
    _setAllAcsTempsAndSwitches();
    _emitAcsStatus(emit);
    _runDeBouncerForAllAcs(code: 'switch', value: event.value);
  }

  void _increaseAllTemp(IncreaseAllTemp event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    double tempValue = event.value + 0.5;
    int value = (tempValue * 10).toInt();

    if (!_checkTemperatureValue(tempValue, emit)) {
      return;
    }

    if (deviceStatusList.length == devicesList.length) {
      for (int i = 0; i < deviceStatusList.length; i++) {
        deviceStatusList[i].tempSet = value;
      }
    }
    _setAllAcsTempsAndSwitches();
    _emitAcsStatus(emit);
    _runDeBouncerForAllAcs(code: 'temp_set', value: value);
  }

  void _decreaseAllTemp(DecreaseAllTemp event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());

    double tempValue = event.value - 0.5;
    int value = (tempValue * 10).toInt();

    if (!_checkTemperatureValue(tempValue, emit)) {
      return;
    }

    if (deviceStatusList.length == devicesList.length) {
      for (int i = 0; i < deviceStatusList.length; i++) {
        deviceStatusList[i].tempSet = value;
      }
    }
    _setAllAcsTempsAndSwitches();
    _emitAcsStatus(emit);
    _runDeBouncerForAllAcs(code: 'temp_set', value: value);
  }

  void _changeLockValue(ChangeLock event, Emitter<AcsState> emit) async {
    emit(AcChangeLoading(acStatusModel: deviceStatus));

    final lockValue = !event.lockBool;
    deviceStatus.childLock = lockValue;
    emit(AcModifyingState(acStatusModel: deviceStatus));

    await _runDeBouncerForOneDevice(deviceId: acId, code: 'child_lock', value: lockValue);
  }

  void _increaseCoolTo(IncreaseCoolToTemp event, Emitter<AcsState> emit) async {
    emit(AcChangeLoading(acStatusModel: deviceStatus));

    double tempValue = event.value + 0.5;
    int value = (tempValue * 10).toInt();

    if (!_checkTemperatureValue(tempValue, emit)) {
      return;
    }

    if (allAcsPage) {
      emit(AcsLoadingState());
      for (AcStatusModel ac in deviceStatusList) {
        if (ac.uuid == event.deviceId) {
          ac.tempSet = value;
        }
      }
      _setAllAcsTempsAndSwitches();
      _emitAcsStatus(emit);
    } else {
      emit(AcChangeLoading(acStatusModel: deviceStatus));
      deviceStatus.tempSet = value;
      emit(AcModifyingState(acStatusModel: deviceStatus));
    }

    await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'temp_set', value: value);
  }

  void _decreaseCoolTo(DecreaseCoolToTemp event, Emitter<AcsState> emit) async {
    emit(AcChangeLoading(acStatusModel: deviceStatus));

    double tempValue = event.value - 0.5;
    int value = (tempValue * 10).toInt();

    if (!_checkTemperatureValue(tempValue, emit)) {
      return;
    }

    if (allAcsPage) {
      emit(AcsLoadingState());
      for (AcStatusModel ac in deviceStatusList) {
        if (ac.uuid == event.deviceId) {
          ac.tempSet = value;
        }
      }
      _setAllAcsTempsAndSwitches();
      _emitAcsStatus(emit);
    } else {
      emit(AcChangeLoading(acStatusModel: deviceStatus));
      deviceStatus.tempSet = value;
      emit(AcModifyingState(acStatusModel: deviceStatus));
    }

    await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'temp_set', value: value);
  }

  void _changeAcMode(ChangeAcMode event, Emitter<AcsState> emit) async {
    final tempMode = tempModesMap[getNextItem(tempModesMap, event.tempModes)]!;
    if (allAcsPage) {
      emit(AcsLoadingState());
      for (AcStatusModel ac in deviceStatusList) {
        if (ac.uuid == event.deviceId) {
          ac.modeString = getACModeString(tempMode);
          ac.acMode = AcStatusModel.getACMode(getACModeString(tempMode));
        }
      }
      _emitAcsStatus(emit);
    } else {
      emit(AcChangeLoading(acStatusModel: deviceStatus));
      deviceStatus.modeString = getACModeString(tempMode);
      deviceStatus.acMode = AcStatusModel.getACMode(getACModeString(tempMode));
      emit(AcModifyingState(acStatusModel: deviceStatus));
    }

    await _runDeBouncerForOneDevice(
        deviceId: event.deviceId, code: 'mode', value: getACModeString(tempMode));
  }

  void _changeFanSpeed(ChangeFanSpeed event, Emitter<AcsState> emit) async {
    emit(AcChangeLoading(acStatusModel: deviceStatus));

    final fanSpeed = event.fanSpeeds;

    if (allAcsPage) {
      emit(AcsLoadingState());
      for (AcStatusModel ac in deviceStatusList) {
        if (ac.uuid == event.deviceId) {
          ac.fanSpeedsString = getNextFanSpeedKey(fanSpeed);
          ac.acFanSpeed = AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed));
        }
      }
      _emitAcsStatus(emit);
    } else {
      emit(AcChangeLoading(acStatusModel: deviceStatus));
      deviceStatus.fanSpeedsString = getNextFanSpeedKey(fanSpeed);
      deviceStatus.acFanSpeed = AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed));
      emit(AcModifyingState(acStatusModel: deviceStatus));
    }

    await _runDeBouncerForOneDevice(
        deviceId: event.deviceId, code: 'level', value: getNextFanSpeedKey(fanSpeed));
  }

  String getACModeString(TempModes value) {
    if (value == TempModes.cold) {
      return 'cold';
    } else if (value == TempModes.hot) {
      return 'hot';
    } else if (value == TempModes.wind) {
      return 'wind';
    } else {
      return 'cold';
    }
  }

  void _setAllAcsTempsAndSwitches() {
    allAcsOn = true;
    allTempSame = true;
    if (deviceStatusList.isNotEmpty) {
      int temp = deviceStatusList.first.tempSet;
      for (var element in deviceStatusList) {
        if (!element.acSwitch) {
          allAcsOn = false;
        }
        if (element.tempSet != temp) {
          allTempSame = false;
        }
      }
      if (allTempSame) {
        globalTemp = temp;
      }
    }
  }

  _runDeBouncerForAllAcs({required String code, required dynamic value}) {
    if (_timer != null) {
      _timer!.cancel();
    }
    _timer = Timer(const Duration(seconds: 1), () async {
      if (deviceStatusList.length == devicesList.length) {
        for (int i = 0; i < deviceStatusList.length; i++) {
          try {
            await DevicesAPI.controlDevice(
                DeviceControlModel(deviceId: devicesList[i].uuid, code: code, value: value),
                devicesList[i].uuid ?? '');
          } catch (_) {
            await Future.delayed(const Duration(milliseconds: 500));
            add(const AcsInitial(allAcs: true));
          }
        }
      }
    });
  }

  _runDeBouncerForOneDevice({
    required String deviceId,
    required String code,
    required dynamic value,
  }) {
    if (_timer != null) {
      _timer!.cancel();
    }
    _timer = Timer(const Duration(seconds: 1), () async {
      try {
        final response = await DevicesAPI.controlDevice(
            DeviceControlModel(deviceId: allAcsPage ? deviceId : acId, code: code, value: value),
            allAcsPage ? deviceId : acId);

        if (!response['success']) {
          add(AcsInitial(allAcs: allAcsPage));
        }
      } catch (_) {
        await Future.delayed(const Duration(milliseconds: 500));
        add(AcsInitial(allAcs: allAcsPage));
      }
    });
  }

  bool _checkTemperatureValue(double value, Emitter<AcsState> emit) {
    if (value >= 20 && value <= 30) {
      return true;
    } else {
      emit(const AcsFailedState(errorMessage: 'The temperature must be between 20 and 30'));
      emit(GetAllAcsStatusState(
          allAcsStatues: deviceStatusList,
          allAcs: devicesList,
          allOn: allAcsOn,
          allTempSame: allTempSame,
          temp: globalTemp));
      return false;
    }
  }

  _emitAcsStatus(Emitter<AcsState> emit) {
    emit(GetAllAcsStatusState(
        allAcsStatues: deviceStatusList,
        allAcs: devicesList,
        allOn: allAcsOn,
        allTempSame: allTempSame,
        temp: globalTemp));
  }

  void _setCounterValue(SetCounterValue event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    int seconds = 0;
    try {
      seconds = event.seconds;
      final response = await DevicesAPI.controlDevice(
          DeviceControlModel(deviceId: acId, code: 'countdown_time', value: event.duration), acId);

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

  void _getCounterValue(GetCounterEvent event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    var response = await DevicesAPI.getDeviceStatus(acId);
    List<StatusModel> statusModelList = [];
    for (var status in response['status']) {
      statusModelList.add(StatusModel.fromJson(status));
    }
    deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList);
    deviceStatus.countdown1;
    var duration;
    if (deviceStatus.countdown1 == 5) {
      duration = const Duration(minutes: 30);
      var countNum = duration.inSeconds;
      _onStartTimer(countNum);
    } else if (deviceStatus.countdown1 > 5) {
      duration = Duration(minutes: deviceStatus.countdown1 * 6);
      var countNum = duration.inSeconds;
      _onStartTimer(countNum);
    } else {
      _timer?.cancel();
      emit(TimerRunComplete());
    }
  }

  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<AcsState> emit) {
    if (event.remainingTime > 0) {
      emit(TimerRunInProgress(event.remainingTime));
    } else {
      _timer?.cancel();
      emit(TimerRunComplete());
    }
  }

  void _onClose(OnClose event, Emitter<AcsState> emit) {
    _timer?.cancel(); // Cancel the timer
  }
}
