import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routiens/models/routine_model.dart'; import 'package:syncrow_web/services/routines_api.dart'; part 'routine_event.dart'; part 'routine_state.dart'; const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6'; const communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9'; class RoutineBloc extends Bloc { RoutineBloc() : super(const RoutineState()) { on(_onAddToIfContainer); on(_onAddToThenContainer); on(_onLoadScenes); on(_onLoadAutomation); on(_onAddFunctionsToRoutine); on(_onSearchRoutines); on(_onAddSelectedIcon); on(_onCreateScene); on(_onRemoveDragCard); on(_changeOperatorOperator); on(_onEffectiveTimeEvent); on(_onCreateAutomation); on(_onSetRoutineName); on(_onResetRoutineState); // on(_onRemoveFunction); // on(_onClearFunctions); } void _onAddToIfContainer(AddToIfContainer event, Emitter emit) { final updatedIfItems = List>.from(state.ifItems); // Find the index of the item in teh current itemsList int index = updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); // Replace the map if the index is valid if (index != -1) { updatedIfItems[index] = event.item; } else { updatedIfItems.add(event.item); } if (event.isTabToRun) { emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); } else { emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); } } void _onAddToThenContainer(AddToThenContainer event, Emitter emit) { final currentItems = List>.from(state.thenItems); // Find the index of the item in teh current itemsList int index = currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); // Replace the map if the index is valid if (index != -1) { currentItems[index] = event.item; } else { currentItems.add(event.item); } emit(state.copyWith(thenItems: currentItems)); } void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter emit) { try { if (event.functions.isEmpty) return; List selectedFunction = List.from(event.functions); Map> currentSelectedFunctions = Map>.from(state.selectedFunctions); if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) { List currentFunctions = List.from(currentSelectedFunctions[event.uniqueCustomId] ?? []); List functionCode = []; for (int i = 0; i < selectedFunction.length; i++) { for (int j = 0; j < currentFunctions.length; j++) { if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) { currentFunctions[j] = selectedFunction[i]; if (!functionCode.contains(currentFunctions[j].functionCode)) { functionCode.add(currentFunctions[j].functionCode); } } } } for (int i = 0; i < functionCode.length; i++) { selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]); } currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions) ..addAll(selectedFunction); } else { currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions); } emit(state.copyWith(selectedFunctions: currentSelectedFunctions)); } catch (e) { debugPrint('Error adding functions: $e'); } } Future _onLoadScenes(LoadScenes event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); try { final scenes = await SceneApi.getScenesByUnitId(event.unitId, event.communityId); emit(state.copyWith( scenes: scenes, isLoading: false, )); } catch (e) { emit(state.copyWith( isLoading: false, loadScenesErrorMessage: 'Failed to load scenes', errorMessage: '', loadAutomationErrorMessage: '', )); } } Future _onLoadAutomation(LoadAutomation event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); try { final automations = await SceneApi.getAutomationByUnitId(event.unitId); if (automations.isNotEmpty) { emit(state.copyWith( automations: automations, isLoading: false, )); } else { emit(state.copyWith( isLoading: false, loadAutomationErrorMessage: 'Failed to load automations', errorMessage: '', loadScenesErrorMessage: '', )); } } catch (e) { emit(state.copyWith( isLoading: false, loadAutomationErrorMessage: 'Failed to load automations', errorMessage: '', loadScenesErrorMessage: '', )); } } FutureOr _onSearchRoutines(SearchRoutines event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); await Future.delayed(const Duration(seconds: 1)); emit(state.copyWith(isLoading: false, errorMessage: null)); emit(state.copyWith(searchText: event.query)); } FutureOr _onAddSelectedIcon(AddSelectedIcon event, Emitter emit) { emit(state.copyWith(selectedIcon: event.icon)); } bool _isFirstActionDelay(List> actions) { if (actions.isEmpty) return false; return actions.first['deviceId'] == 'delay'; } Future _onCreateScene(CreateSceneEvent event, Emitter emit) async { try { // Check if first action is delay if (_isFirstActionDelay(state.thenItems)) { emit(state.copyWith( errorMessage: 'Cannot have delay as the first action', isLoading: false, )); return; } emit(state.copyWith(isLoading: true)); final actions = state.thenItems.expand((item) { final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; return functions.map((function) { if (function.functionCode == 'automation') { return CreateSceneAction( entityId: function.entityId, actionExecutor: function.value, executorProperty: null, ); } if (item['deviceId'] == 'delay') { return CreateSceneAction( entityId: function.entityId, actionExecutor: 'delay', executorProperty: CreateSceneExecutorProperty( functionCode: '', functionValue: '', delaySeconds: int.tryParse(function.value.toString()) ?? 0, ), ); } return CreateSceneAction( entityId: function.entityId, actionExecutor: 'device_issue', executorProperty: CreateSceneExecutorProperty( functionCode: function.functionCode, functionValue: function.value, delaySeconds: 0, ), ); }); }).toList(); final createSceneModel = CreateSceneModel( spaceUuid: spaceId, iconId: state.selectedIcon ?? '', showInDevice: true, sceneName: state.routineName!, decisionExpr: 'and', actions: actions, ); final result = await SceneApi.createScene(createSceneModel); if (result['success']) { emit(_resetState()); add(const LoadScenes(spaceId, communityId)); } else { emit(state.copyWith( isLoading: false, errorMessage: result['message'], )); } } catch (e) { emit(state.copyWith( isLoading: false, errorMessage: 'Something went wrong', )); } } Future _onCreateAutomation(CreateAutomationEvent event, Emitter emit) async { try { if (state.routineName == null || state.routineName!.isEmpty) { emit(state.copyWith( errorMessage: 'Automation name is required', )); return; } emit(state.copyWith(isLoading: true)); final conditions = state.ifItems.expand((item) { final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; return functions.map((function) { return Condition( code: state.selectedFunctions[item['uniqueCustomId']]!.indexOf( function, ) + 1, entityId: function.entityId, entityType: 'device_report', expr: ConditionExpr( statusCode: function.functionCode, comparator: function.condition ?? '==', statusValue: function.value, ), ); }); }).toList(); if (conditions.isEmpty) { emit(state.copyWith( isLoading: false, errorMessage: 'At least one condition is required', )); return; } final actions = state.thenItems.expand((item) { final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; return functions.map((function) { if (function.functionCode == 'automation') { return AutomationAction( entityId: function.entityId, actionExecutor: function.value, ); } if (item['deviceId'] == 'delay') { return AutomationAction( entityId: function.entityId, actionExecutor: 'delay', executorProperty: ExecutorProperty( delaySeconds: int.tryParse(function.value.toString()) ?? 0, ), ); } return AutomationAction( entityId: function.entityId, actionExecutor: 'device_issue', executorProperty: ExecutorProperty( functionCode: function.functionCode, functionValue: function.value, ), ); }); }).toList(); final createAutomationModel = CreateAutomationModel( spaceUuid: spaceId, automationName: state.routineName!, decisionExpr: state.selectedAutomationOperator, effectiveTime: EffectiveTime( start: state.effectiveTime?.start ?? '00:00', end: state.effectiveTime?.end ?? '23:59', loops: state.effectiveTime?.loops ?? '1111111', ), conditions: conditions, actions: actions, ); final result = await SceneApi.createAutomation(createAutomationModel); if (result['success']) { emit(_resetState()); add(const LoadAutomation(spaceId)); } else { emit(state.copyWith( isLoading: false, errorMessage: result['message'], )); } } catch (e) { emit(state.copyWith( isLoading: false, errorMessage: 'Something went wrong', )); } } FutureOr _onRemoveDragCard(RemoveDragCard event, Emitter emit) { if (event.isFromThen) { final thenItems = List>.from(state.thenItems); final selectedFunctions = Map>.from(state.selectedFunctions); thenItems.removeAt(event.index); selectedFunctions.remove(event.key); emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions)); } else { final ifItems = List>.from(state.ifItems); final selectedFunctions = Map>.from(state.selectedFunctions); ifItems.removeAt(event.index); selectedFunctions.remove(event.key); emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions)); } } FutureOr _changeOperatorOperator(ChangeAutomationOperator event, Emitter emit) { emit(state.copyWith( selectedAutomationOperator: event.operator, )); } FutureOr _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter emit) { emit(state.copyWith(effectiveTime: event.effectiveTime)); } FutureOr _onSetRoutineName(SetRoutineName event, Emitter emit) { emit(state.copyWith(routineName: event.name)); } RoutineState _resetState() { return const RoutineState( ifItems: [], thenItems: [], selectedFunctions: {}, scenes: [], automations: [], isLoading: false, errorMessage: null, loadScenesErrorMessage: null, loadAutomationErrorMessage: null, searchText: '', selectedIcon: null, isTabToRun: false, isAutomation: false, selectedAutomationOperator: 'AND', effectiveTime: null, routineName: null, ); } FutureOr _onResetRoutineState(ResetRoutineState event, Emitter emit) { emit(_resetState()); } }