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

import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_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';

class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
  final String deviceId;
  final ControlDeviceService controlDeviceService;
  final BatchControlDevicesService batchControlDevicesService;

  late WallSensorModel deviceStatus;

  WallSensorBloc({
    required this.deviceId,
    required this.controlDeviceService,
    required this.batchControlDevicesService,
  }) : super(WallSensorInitialState()) {
    on<WallSensorFetchStatusEvent>(_fetchWallSensorStatus);
    on<WallSensorFetchBatchStatusEvent>(_fetchWallSensorBatchControl);
    on<WallSensorChangeValueEvent>(_changeValue);
    on<WallSensorBatchControlEvent>(_onBatchControl);
    on<GetDeviceReportsEvent>(_getDeviceReports);
    on<ShowDescriptionEvent>(_showDescription);
    on<BackToGridViewEvent>(_backToGridView);
    on<WallSensorFactoryResetEvent>(_onFactoryReset);
    on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate);
  }

  Future<void> _fetchWallSensorStatus(
    WallSensorFetchStatusEvent event,
    Emitter<WallSensorState> emit,
  ) async {
    emit(WallSensorLoadingInitialState());
    try {
      final response = await DevicesManagementApi().getDeviceStatus(deviceId);
      deviceStatus = WallSensorModel.fromJson(response.status);
      _listenToChanges(deviceId);
      emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
    } catch (e) {
      emit(WallSensorFailedState(error: e.toString()));
    }
  }

  Future<void> _fetchWallSensorBatchControl(
    WallSensorFetchBatchStatusEvent event,
    Emitter<WallSensorState> emit,
  ) async {
    emit(WallSensorLoadingInitialState());
    try {
      final response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
      deviceStatus = WallSensorModel.fromJson(response.status);
      emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
    } catch (e) {
      emit(WallSensorFailedState(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 = WallSensorModel.fromJson(statusList);
        if (!isClosed) {
          add(WallSensorRealtimeUpdateEvent(deviceStatus));
        }
      });
    } catch (_) {
      log(
        'Error listening to changes',
        name: 'WallSensorBloc._listenToChanges',
      );
    }
  }

  Future<void> _changeValue(
    WallSensorChangeValueEvent event,
    Emitter<WallSensorState> emit,
  ) async {
    emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
    _updateLocalValue(event.code, event.value);
    emit(WallSensorUpdateState(wallSensorModel: deviceStatus));

    try {
      await controlDeviceService.controlDevice(
        deviceUuid: deviceId,
        status: Status(code: event.code, value: event.value),
      );
    } catch (e) {
      _updateLocalValue(event.code, event.value == 0 ? 1 : 0);
      emit(WallSensorFailedState(error: e.toString()));
    }
  }

  Future<void> _onBatchControl(
    WallSensorBatchControlEvent event,
    Emitter<WallSensorState> emit,
  ) async {
    emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
    _updateLocalValue(event.code, event.value);
    emit(WallSensorUpdateState(wallSensorModel: deviceStatus));

    try {
      await batchControlDevicesService.batchControlDevices(
        uuids: event.deviceIds,
        code: event.code,
        value: event.value,
      );
    } catch (e) {
      _updateLocalValue(event.code, !event.value);
      emit(WallSensorFailedState(error: e.toString()));
    }
  }

  Future<void> _getDeviceReports(
    GetDeviceReportsEvent event,
    Emitter<WallSensorState> emit,
  ) async {
    emit(DeviceReportsLoadingState());
    try {
      final reports = await DevicesManagementApi.getDeviceReports(
        deviceId,
        event.code,
      );
      emit(DeviceReportsState(deviceReport: reports, code: event.code));
    } catch (e) {
      emit(DeviceReportsFailedState(error: e.toString()));
    }
  }

  void _showDescription(
    ShowDescriptionEvent event,
    Emitter<WallSensorState> emit,
  ) {
    emit(WallSensorShowDescriptionState(description: event.description));
  }

  void _backToGridView(
    BackToGridViewEvent event,
    Emitter<WallSensorState> emit,
  ) {
    emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
  }

  Future<void> _onFactoryReset(
    WallSensorFactoryResetEvent event,
    Emitter<WallSensorState> emit,
  ) async {
    emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
    try {
      final response = await DevicesManagementApi().factoryReset(
        event.factoryReset,
        event.deviceId,
      );
      if (!response) {
        emit(const WallSensorFailedState(error: 'Failed to reset device'));
      } else {
        add(WallSensorFetchStatusEvent());
      }
    } catch (e) {
      emit(WallSensorFailedState(error: e.toString()));
    }
  }

  void _onRealtimeUpdate(
    WallSensorRealtimeUpdateEvent event,
    Emitter<WallSensorState> emit,
  ) {
    emit(WallSensorUpdateState(wallSensorModel: event.deviceStatus));
  }

  void _updateLocalValue(String code, dynamic value) {
    switch (code) {
      case 'far_detection':
        deviceStatus.farDetection = value;
        break;
      case 'motionless_sensitivity':
        deviceStatus.motionlessSensitivity = value;
        break;
      case 'motion_sensitivity_value':
        deviceStatus.motionSensitivity = value;
        break;
      case 'no_one_time':
        deviceStatus.noBodyTime = value;
        break;
    }
  }
}
