import 'dart:async';
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_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_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 'curtain_module_event.dart';
part 'curtain_module_state.dart';

class CurtainModuleBloc extends Bloc<CurtainModuleEvent, CurtainModuleState> {
  final ControlDeviceService controlDeviceService;
  final BatchControlDevicesService batchControlDevicesService;
  StreamSubscription<DatabaseEvent>? _firebaseSubscription;

  CurtainModuleBloc({
    required this.controlDeviceService,
    required this.batchControlDevicesService,
  }) : super(CurtainModuleInitial()) {
    on<FetchCurtainModuleStatusEvent>(_onFetchCurtainModuleStatusEvent);
    on<SendCurtainPercentToApiEvent>(_onSendCurtainPercentToApiEvent);
    on<OpenCurtainEvent>(_onOpenCurtainEvent);
    on<CloseCurtainEvent>(_onCloseCurtainEvent);
    on<StopCurtainEvent>(_onStopCurtainEvent);
    on<ChangeTimerControlEvent>(_onChangeTimerControlEvent);
    on<CurCalibrationEvent>(_onChageCurCalibrationEvent);
    on<ChangeElecMachineryModeEvent>(_onChangeElecMachineryModeEvent);
    on<ChangeControlBackEvent>(_onChangeControlBackEvent);
    on<ChangeControlBackModeEvent>(_onChangeControlBackModeEvent);
    on<ChangeCurtainModuleStatusEvent>(_onChangeCurtainModuleStatusEvent);
    //batch
    on<CurtainModuleFetchBatchStatusEvent>(_onFetchCurtainModuleBatchStatus);
    on<SendCurtainBatchPercentToApiEvent>(_onSendCurtainBatchPercentToApiEvent);
    on<OpenCurtainBatchEvent>(_onOpenCurtainBatchEvent);
    on<CloseCurtainBatchEvent>(_onCloseCurtainBatchEvent);
    on<StopCurtainBatchEvent>(_onStopCurtainBatchEvent);
    on<CurtainModuleFactoryReset>(_onFactoryReset);
  }

  Future<void> _onFetchCurtainModuleStatusEvent(
    FetchCurtainModuleStatusEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    emit(CurtainModuleLoading());
    final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
    final result = Map.fromEntries(
      status.status.map((element) => MapEntry(element.code, element.value)),
    );

    emit(CurtainModuleStatusLoaded(
      curtainModuleStatus: CurtainModuleStatusModel.fromJson(result),
    ));
    Map<String, dynamic> statusMap = {};
    final ref =
        FirebaseDatabase.instance.ref('device-status/${event.deviceId}');
    final stream = ref.onValue;

    stream.listen((DatabaseEvent DatabaseEvent) async {
      if (DatabaseEvent.snapshot.value == null) return;

      Map<dynamic, dynamic> usersMap =
          DatabaseEvent.snapshot.value as Map<dynamic, dynamic>;

      List<Status> statusList = [];

      usersMap['status'].forEach((element) {
        statusList.add(Status(code: element['code'], value: element['value']));
      });

      statusMap = {
        for (final element in statusList) element.code: element.value,
      };
      if (!isClosed) {
        add(
          ChangeCurtainModuleStatusEvent(
            deviceId: event.deviceId,
            status: CurtainModuleStatusModel.fromJson(statusMap),
          ),
        );
      }
    });
  }

  Future<void> _onChangeCurtainModuleStatusEvent(
    ChangeCurtainModuleStatusEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    emit(CurtainModuleLoading());
    emit(CurtainModuleStatusLoaded(curtainModuleStatus: event.status));
  }

  Future<void> _onSendCurtainPercentToApiEvent(
    SendCurtainPercentToApiEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: event.status,
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to send control command: $e'));
    }
  }

  Future<void> _onOpenCurtainEvent(
    OpenCurtainEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(code: 'control', value: 'open'),
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to open curtain: $e'));
    }
  }

  Future<void> _onCloseCurtainEvent(
    CloseCurtainEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(code: 'control', value: 'close'),
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to close curtain: $e'));
    }
  }

  Future<void> _onStopCurtainEvent(
    StopCurtainEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(code: 'control', value: 'stop'),
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to stop curtain: $e'));
    }
  }

  Future<void> _onChangeTimerControlEvent(
    ChangeTimerControlEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      if (event.timControl < 10 || event.timControl > 120) {
        emit(const CurtainModuleError(
            message: 'Timer control value must be between 10 and 120'));
        return;
      }
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(
          code: 'tr_timecon',
          value: event.timControl,
        ),
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to change timer control: $e'));
    }
  }

  Future<void> _onChageCurCalibrationEvent(
    CurCalibrationEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(code: 'cur_calibration', value: 'start'),
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to start calibration: $e'));
    }
  }

  Future<void> _onChangeElecMachineryModeEvent(
    ChangeElecMachineryModeEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(
          code: 'elec_machinery_mode',
          value: event.elecMachineryMode,
        ),
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to change mode: $e'));
    }
  }

  Future<void> _onChangeControlBackEvent(
    ChangeControlBackEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(
          code: 'control_back',
          value: event.controlBack,
        ),
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to change control back: $e'));
    }
  }

  Future<void> _onChangeControlBackModeEvent(
    ChangeControlBackModeEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await controlDeviceService.controlDevice(
        deviceUuid: event.deviceId,
        status: Status(
          code: 'control_back_mode',
          value: event.controlBackMode,
        ),
      );
    } catch (e) {
      emit(CurtainModuleError(
          message: 'Failed to change control back mode: $e'));
    }
  }

  FutureOr<void> _onFetchCurtainModuleBatchStatus(
    CurtainModuleFetchBatchStatusEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    emit(CurtainModuleLoading());
    try {
      final status =
          await DevicesManagementApi().getBatchStatus(event.devicesIds);

      final result = Map.fromEntries(
        status.status.map((element) => MapEntry(element.code, element.value)),
      );

      emit(CurtainModuleStatusLoaded(
        curtainModuleStatus: CurtainModuleStatusModel.fromJson(result),
      ));

      Map<String, dynamic> statusMap = {};
      final ref = FirebaseDatabase.instance
          .ref('device-status/${event.devicesIds.first}');
      final stream = ref.onValue;

      stream.listen((DatabaseEvent DatabaseEvent) async {
        if (DatabaseEvent.snapshot.value == null) return;

        Map<dynamic, dynamic> usersMap =
            DatabaseEvent.snapshot.value as Map<dynamic, dynamic>;

        List<Status> statusList = [];

        usersMap['status'].forEach((element) {
          statusList
              .add(Status(code: element['code'], value: element['value']));
        });

        statusMap = {
          for (final element in statusList) element.code: element.value,
        };
        if (!isClosed) {
          add(
            ChangeCurtainModuleStatusEvent(
              deviceId: event.devicesIds.first,
              status: CurtainModuleStatusModel.fromJson(statusMap),
            ),
          );
        }
      });
    } catch (e) {
      emit(CurtainModuleError(message: e.toString()));
    }
  }

  Future<void> _onSendCurtainBatchPercentToApiEvent(
    SendCurtainBatchPercentToApiEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await batchControlDevicesService.batchControlDevices(
        uuids: event.devicesId,
        code: event.status.code,
        value: event.status.value,
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to send control command: $e'));
    }
  }

  Future<void> _onOpenCurtainBatchEvent(
    OpenCurtainBatchEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await batchControlDevicesService.batchControlDevices(
        uuids: event.devicesId,
        code: 'control',
        value: 'open',
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to open curtain: $e'));
    }
  }

  Future<void> _onCloseCurtainBatchEvent(
    CloseCurtainBatchEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await batchControlDevicesService.batchControlDevices(
        uuids: event.devicesId,
        code: 'control',
        value: 'close',
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to close curtain: $e'));
    }
  }

  Future<void> _onStopCurtainBatchEvent(
    StopCurtainBatchEvent event,
    Emitter<CurtainModuleState> emit,
  ) async {
    try {
      await batchControlDevicesService.batchControlDevices(
        uuids: event.devicesId,
        code: 'control',
        value: 'stop',
      );
    } catch (e) {
      emit(CurtainModuleError(message: 'Failed to stop curtain: $e'));
    }
  }

  Future<void> _onFactoryReset(
    CurtainModuleFactoryReset event,
    Emitter<CurtainModuleState> emit,
  ) async {
    emit(CurtainModuleLoading());
    try {
      final response = await DevicesManagementApi().factoryReset(
        event.factoryReset,
        event.deviceId,
      );
      if (!response) {
        emit(const CurtainModuleError(message: 'Failed'));
      } else {
        add(
          FetchCurtainModuleStatusEvent(deviceId: event.deviceId),
        );
      }
    } catch (e) {
      emit(CurtainModuleError(message: e.toString()));
    }
  }

  @override
  Future<void> close() async {
    await _firebaseSubscription?.cancel();
    return super.close();
  }
}
