import 'dart:async';
import 'dart:developer';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';

part 'flush_mounted_presence_sensor_event.dart';
part 'flush_mounted_presence_sensor_state.dart';

class FlushMountedPresenceSensorBloc
    extends Bloc<FlushMountedPresenceSensorEvent, FlushMountedPresenceSensorState> {
  final String deviceId;
  final ControlDeviceService controlDeviceService;
  final BatchControlDevicesService batchControlDevicesService;

  late FlushMountedPresenceSensorModel deviceStatus;
  FlushMountedPresenceSensorBloc({
    required this.deviceId,
    required this.controlDeviceService,
    required this.batchControlDevicesService,
  }) : super(FlushMountedPresenceSensorInitialState()) {
    on<FlushMountedPresenceSensorFetchStatusEvent>(
      _onFlushMountedPresenceSensorFetchStatusEvent,
    );
    on<FlushMountedPresenceSensorFetchBatchStatusEvent>(
        _onFlushMountedPresenceSensorFetchBatchStatusEvent);
    on<FlushMountedPresenceSensorChangeValueEvent>(
      _onFlushMountedPresenceSensorChangeValueEvent,
    );
    on<FlushMountedPresenceSensorBatchControlEvent>(
      _onFlushMountedPresenceSensorBatchControlEvent,
    );
    on<FlushMountedPresenceSensorGetDeviceReportsEvent>(
        _onFlushMountedPresenceSensorGetDeviceReportsEvent);
    on<FlushMountedPresenceSensorShowDescriptionEvent>(
      _onFlushMountedPresenceSensorShowDescriptionEvent,
    );
    on<FlushMountedPresenceSensorBackToGridViewEvent>(
      _onFlushMountedPresenceSensorBackToGridViewEvent,
    );
    on<FlushMountedPresenceSensorFactoryResetEvent>(
      _onFlushMountedPresenceSensorFactoryResetEvent,
    );
    on<FlushMountedPresenceSensorStatusUpdatedEvent>(
      _onFlushMountedPresenceSensorStatusUpdatedEvent,
    );
  }

  void _onFlushMountedPresenceSensorFetchStatusEvent(
    FlushMountedPresenceSensorFetchStatusEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) async {
    emit(FlushMountedPresenceSensorLoadingInitialState());
    try {
      final response = await DevicesManagementApi().getDeviceStatus(deviceId);
      deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
      emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
      _listenToChanges(deviceId);
    } catch (e) {
      emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
      return;
    }
  }

  Future<void> _onFlushMountedPresenceSensorFetchBatchStatusEvent(
    FlushMountedPresenceSensorFetchBatchStatusEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) async {
    emit(FlushMountedPresenceSensorLoadingInitialState());
    try {
      final response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
      deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
      emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
    } catch (e) {
      emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
    }
  }

  void _listenToChanges(String deviceId) {
    try {
      final ref = FirebaseDatabase.instance.ref(
        'device-status/$deviceId',
      );

      ref.onValue.listen((event) {
        final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;

        List<Status> statusList = [];
        eventsMap['status'].forEach((element) {
          statusList.add(
            Status(code: element['code'], value: element['value']),
          );
        });

        deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList);
        if (!isClosed) {
          add(FlushMountedPresenceSensorStatusUpdatedEvent(deviceStatus));
        }
      });
    } catch (_) {
      log(
        'Error listening to changes',
        name: 'FlushMountedPresenceSensorBloc._listenToChanges',
      );
    }
  }

  void _onFlushMountedPresenceSensorChangeValueEvent(
    FlushMountedPresenceSensorChangeValueEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) async {
    emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
    _updateDeviceFunctionFromCode(event.code, event.value);
    emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: deviceId,
        status: Status(code: event.code, value: event.value),
      );
    } catch (_) {
      await _reloadDeviceStatus();
    }
  }

  Future<void> _onFlushMountedPresenceSensorBatchControlEvent(
    FlushMountedPresenceSensorBatchControlEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) async {
    emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
    _updateDeviceFunctionFromCode(event.code, event.value);
    emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));

    try {
      await batchControlDevicesService.batchControlDevices(
        uuids: event.deviceIds,
        code: event.code,
        value: event.value,
      );
    } catch (_) {
      await _reloadDeviceStatus();
    }
  }

  void _updateDeviceFunctionFromCode(String code, int value) {
    switch (code) {
      case FlushMountedPresenceSensorModel.codeFarDetection:
        deviceStatus.farDetection = value;
        break;
      case FlushMountedPresenceSensorModel.codeSensitivity:
        deviceStatus.sensitivity = value;
        break;
      case FlushMountedPresenceSensorModel.codeNoneDelay:
        deviceStatus.noneDelay = value;
        break;
      case FlushMountedPresenceSensorModel.codePresenceDelay:
        deviceStatus.presenceDelay = value;
        break;
      case FlushMountedPresenceSensorModel.codeNearDetection:
        deviceStatus.nearDetection = value;
        break;
      case FlushMountedPresenceSensorModel.codeOccurDistReduce:
        deviceStatus.occurDistReduce = value;
        break;
      case FlushMountedPresenceSensorModel.codeSensiReduce:
        deviceStatus.sensiReduce = value;
        break;
      default:
        return;
    }
  }

  Future<void> _reloadDeviceStatus() async {
    await Future.delayed(const Duration(milliseconds: 500), () {
      add(FlushMountedPresenceSensorFetchStatusEvent());
    });
  }

  Future<void> _onFlushMountedPresenceSensorGetDeviceReportsEvent(
    FlushMountedPresenceSensorGetDeviceReportsEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) async {
    emit(FlushMountedPresenceSensorDeviceReportsLoadingState());

    try {
      await DevicesManagementApi.getDeviceReports(deviceId, event.code)
          .then((value) {
        emit(FlushMountedPresenceSensorDeviceReportsState(
            deviceReport: value, code: event.code));
      });
    } catch (e) {
      emit(FlushMountedPresenceSensorDeviceReportsFailedState(error: e.toString()));
      return;
    }
  }

  void _onFlushMountedPresenceSensorShowDescriptionEvent(
    FlushMountedPresenceSensorShowDescriptionEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) {
    emit(FlushMountedPresenceSensorShowDescriptionState(
        description: event.description));
  }

  void _onFlushMountedPresenceSensorBackToGridViewEvent(
    FlushMountedPresenceSensorBackToGridViewEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) {
    emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
  }

  Future<void> _onFlushMountedPresenceSensorFactoryResetEvent(
    FlushMountedPresenceSensorFactoryResetEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) async {
    emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
    try {
      final response = await DevicesManagementApi().factoryReset(
        event.factoryReset,
        event.deviceId,
      );
      if (!response) {
        emit(
          const FlushMountedPresenceSensorFailedState(
            error: 'Something went wrong with factory reset, please try again',
          ),
        );
      } else {
        emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
      }
    } catch (e) {
      emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
    }
  }

  void _onFlushMountedPresenceSensorStatusUpdatedEvent(
    FlushMountedPresenceSensorStatusUpdatedEvent event,
    Emitter<FlushMountedPresenceSensorState> emit,
  ) {
    deviceStatus = event.model;
    emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
  }
}
