import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/auth/model/project_model.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart';
import 'package:syncrow_app/features/scene/helper/scene_operations_data_helper.dart';
import 'package:syncrow_app/features/scene/model/create_automation_model.dart';
import 'package:syncrow_app/features/scene/model/create_scene_model.dart';
import 'package:syncrow_app/features/scene/model/icon_model.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/services/api/scene_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';

part 'create_scene_event.dart';
part 'create_scene_state.dart';

class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
    with SceneOperationsDataHelper {
  CreateSceneBloc() : super(CreateSceneInitial()) {
    on<CreateSceneWithTasksEvent>(_createSceneWithTasks);
    on<AddTaskEvent>(_onAddSceneTask);
    on<SelectedValueEvent>(_selectedValue);
    on<RemoveTaskByIdEvent>(_removeTaskById);
    on<ClearTaskListEvent>(_clearTaskList);
    on<ClearTempTaskListEvent>(_clearTempTaskList);
    on<FetchSceneTasksEvent>(_fetchSceneTasks);
    on<TempHoldSceneTasksEvent>(_onTempHoldSceneTask);
    on<RemoveTempTaskByIdEvent>(_removeTempTaskById);
    on<RemoveFromSelectedValueById>(_removeFromSelectedValueById);
    on<DeleteSceneEvent>(_deleteScene);
    on<UpdateTaskEvent>(_updateTaskValue);
    on<SelectConditionEvent>(_selectConditionRule);
    on<SceneTypeEvent>(_sceneTypeEvent);
    on<EffectiveTimePeriodEvent>(_onEffectiveTimeEvent);
    on<SceneIconEvent>(_fetchIconScene);
    on<IconSelected>(_iconSelected);
    on<ShowOnDeviceClicked>(_showInDeviceClicked);
    on<ClearTabToRunSetting>(_clearTabToRunSetting);
  }

  CreateSceneEnum sceneType = CreateSceneEnum.none;

  /// tab to run values and list
  List<SceneStaticFunction> tasksList = [];
  List<SceneStaticFunction> tempTasksList = [];
  final Map<String, dynamic> selectedValues = {};

  /// automation values and list
  List<SceneStaticFunction> automationTasksList = [];
  List<SceneStaticFunction> automationTempTasksList = [];
  final Map<String, dynamic> automationSelectedValues = {};
  final Map<String, String> automationComparatorValues = {};
  String conditionRule = 'or';
  EffectiveTime? effectiveTime;
  List<IconModel> iconModelList = [];
  String selectedIcon = '';
  bool showInDeviceScreen = false;

  FutureOr<void> _onAddSceneTask(
      AddTaskEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    if (event.isAutomation == true) {
      final copyList = List<SceneStaticFunction>.from(automationTempTasksList);
      automationTasksList.addAll(copyList);
      automationTempTasksList.clear();
      automationSelectedValues.clear();
      automationComparatorValues.clear();
      emit(AddSceneTask(
        automationTasksList: automationTasksList,
        tasksList: tasksList,
        condition: conditionRule,
      ));
    } else {
      final copyList = List<SceneStaticFunction>.from(tempTasksList);
      tasksList.addAll(copyList);
      tempTasksList.clear();
      selectedValues.clear();
      emit(AddSceneTask(
        tasksList: tasksList,
        automationTasksList: automationTasksList,
        condition: conditionRule,
      ));
    }
  }

  FutureOr<void> _onTempHoldSceneTask(
      TempHoldSceneTasksEvent event, Emitter<CreateSceneState> emit) {
    if (event.isAutomation == true) {
      addToTempAutomationTaskList(event, emit);
    } else {
      addToTempTaskList(event, emit);
    }
  }

  void addToTempTaskList(
      TempHoldSceneTasksEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    bool updated = false;

    // Check and update if the task exists in tempTasksList
    for (var element in tempTasksList) {
      if (element.code == event.deviceControlModel.code) {
        var updatedElement = element.copyWith(
          deviceType: event.deviceType,
          operationName: event.operation,
          deviceName: event.deviceName,
          icon: event.icon,
          code: event.deviceControlModel.code ?? '',
          deviceId: event.deviceId,
          functionValue: event.deviceControlModel.value,
          operationDialogType: event.operationType,
          operationalValues: [
            SceneOperationalValue(
              value: event.deviceControlModel.value,
              icon: '',
            ),
          ],
        );
        tempTasksList[tempTasksList.indexOf(element)] = updatedElement;
        selectedValues[updatedElement.code] = event.deviceControlModel.value;
        updated = true;
        break;
      }
    }

    if (!updated) {
      /// for smart scene add to view
      for (var element in tasksList) {
        if (element.deviceId == event.deviceControlModel.deviceId &&
            element.code == event.deviceControlModel.code) {
          var updatedElement = element.copyWith(
            deviceType: event.deviceType,
            operationName: event.operation,
            deviceName: event.deviceName,
            icon: event.icon,
            code: event.deviceControlModel.code ?? '',
            deviceId: event.deviceId,
            functionValue: event.deviceControlModel.value,
            operationDialogType: event.operationType,
            operationalValues: [
              SceneOperationalValue(
                value: event.deviceControlModel.value,
                icon: '',
              ),
            ],
          );
          tasksList[tasksList.indexOf(element)] = updatedElement;
          selectedValues[updatedElement.code] = event.deviceControlModel.value;
          updated = true;
          break;
        }
      }
    }

    // Add new element if it doesn't exist in either list
    if (!updated) {
      var newElement = SceneStaticFunction(
        deviceType: event.deviceType,
        operationName: event.operation,
        deviceName: event.deviceName,
        icon: event.icon,
        code: event.deviceControlModel.code ?? '',
        operationDialogType: event.operationType,
        deviceId: event.deviceId,
        functionValue: event.deviceControlModel.value,
        operationalValues: [
          SceneOperationalValue(
            value: event.deviceControlModel.value,
            icon: '',
          ),
        ],
      );
      tempTasksList.add(newElement);
      selectedValues[newElement.code] = event.deviceControlModel.value;
    }

    emit(AddSceneTask(
      tasksList: tasksList,
      automationTasksList: automationTasksList,
      condition: conditionRule,
    ));
  }

  void addToTempAutomationTaskList(
      TempHoldSceneTasksEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    bool updated = false;
    for (var element in automationTempTasksList) {
      if (element.code == event.deviceControlModel.code) {
        // Update the existing function with new values
        var updatedElement = element.copyWith(
          deviceType: element.deviceType,
          operationName: event.operation,
          deviceName: event.deviceName,
          icon: event.icon,
          code: event.deviceControlModel.code ?? '',
          deviceId: event.deviceId,
          functionValue: event.deviceControlModel.value,
          operationDialogType: event.operationType,
          operationalValues: [
            SceneOperationalValue(
              value: event.deviceControlModel.value,
              icon: '',
            ),
          ],
          comparator: automationComparatorValues[element.code],
        );
        automationTempTasksList[automationTempTasksList.indexOf(element)] =
            updatedElement;
        automationSelectedValues[updatedElement.code] =
            event.deviceControlModel.value;
        updated = true;
        break;
      }
    }
    if (!updated) {
      var newElement = SceneStaticFunction(
        deviceType: event.deviceType,
        operationName: event.operation,
        deviceName: event.deviceName,
        icon: event.icon,
        code: event.deviceControlModel.code ?? '',
        operationDialogType: event.operationType,
        deviceId: event.deviceId,
        functionValue: event.deviceControlModel.value,
        operationalValues: [
          SceneOperationalValue(
            value: event.deviceControlModel.value,
            icon: '',
          ),
        ],
        comparator:
            automationComparatorValues[event.deviceControlModel.code] ?? '==',
      );
      automationTempTasksList.add(newElement);
      automationSelectedValues[newElement.code] =
          event.deviceControlModel.value;
    }
    emit(AddSceneTask(
      tasksList: tasksList,
      automationTasksList: automationTasksList,
      condition: conditionRule,
    ));
  }

  FutureOr<void> _selectedValue(
      SelectedValueEvent event, Emitter<CreateSceneState> emit) {
    if (event.isAutomation == true) {
      automationSelectedValues[event.code] = event.value;
      automationComparatorValues[event.code] = event.comparator ?? '==';

      // Update the comparator value for the specific task in automationTasksList
      for (int i = 0; i < automationTasksList.length; i++) {
        if (automationTasksList[i].code == event.code) {
          automationTasksList[i] = automationTasksList[i].copyWith(
            comparator: event.comparator ?? '==',
            functionValue: event.value,
          );
          break;
        }
      }
      for (int i = 0; i < tasksList.length; i++) {
        if (tasksList[i].code == event.code) {
          tasksList[i] = tasksList[i].copyWith(
            comparator: event.comparator ?? '==',
            functionValue: event.value,
          );
          break;
        }
      }
    } else {
      selectedValues[event.code] = event.value;
    }
    emit(SelectedTaskValueState(value: event.value));
    emit(AddSceneTask(
      tasksList: List.from(tasksList),
      automationTasksList: List.from(
        automationTasksList,
      ),
      condition: conditionRule,
    ));
  }

  FutureOr<void> _removeTaskById(
      RemoveTaskByIdEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    if (event.isAutomation == true) {
      for (var element in automationTasksList) {
        if (element.uniqueCustomId == event.taskId) {
          automationTasksList.remove(element);
          emit(AddSceneTask(
            tasksList: tasksList,
            automationTasksList: automationTasksList,
            condition: conditionRule,
          ));
          break;
        }
      }
    } else {
      for (var element in tasksList) {
        if (element.uniqueCustomId == event.taskId) {
          tasksList.remove(element);

          emit(AddSceneTask(
            tasksList: tasksList,
            automationTasksList: automationTasksList,
            condition: conditionRule,
          ));
          break;
        }
      }
    }
  }

  FutureOr<void> _removeTempTaskById(
      RemoveTempTaskByIdEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    if (event.isAutomation == true) {
      for (var element in automationTempTasksList) {
        if (element.uniqueCustomId == event.code) {
          automationTempTasksList.remove(element);
          emit(AddSceneTask(
            tasksList: tasksList,
            automationTasksList: automationTasksList,
            condition: conditionRule,
          ));
          break;
        }
      }
    } else {
      for (var element in tempTasksList) {
        if (element.code == event.code) {
          tempTasksList.remove(element);

          emit(AddSceneTask(
            tasksList: tasksList,
            automationTasksList: automationTasksList,
            condition: conditionRule,
          ));

          break;
        }
      }
    }
  }

  FutureOr<void> _createSceneWithTasks(
      CreateSceneWithTasksEvent event, Emitter<CreateSceneState> emit) async {
    emit(CreateSceneLoading());
    try {
      dynamic response;
      Project? project = HomeCubit.getInstance().project;

      if (event.createSceneModel != null) {
        response = event.updateScene
            ? await SceneApi.updateScene(event.createSceneModel!, event.sceneId)
            : await SceneApi.createScene(event.createSceneModel!);
      } else if (event.createAutomationModel != null) {
        response = event.updateScene
            ? await SceneApi.updateAutomation(event.createAutomationModel!,
                event.sceneId, project?.uuid ?? '')
            : await SceneApi.createAutomation(
                event.createAutomationModel!, project?.uuid ?? '');
      }

      if (response['success'] == true) {
        tasksList.clear();
        tempTasksList.clear();
        selectedValues.clear();
        automationTasksList.clear();
        automationTempTasksList.clear();
        automationSelectedValues.clear();
        automationComparatorValues.clear();
        selectedIcon = '';
        showInDeviceScreen = false;
        effectiveTime =
            EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
        sceneType = CreateSceneEnum.none;
        conditionRule = 'or';
        emit(const CreateSceneWithTasks(success: true));
        CustomSnackBar.greenSnackBar(event.updateScene
            ? 'Scene updated successfully'
            : 'Scene created successfully');
      } else {
        emit(const CreateSceneError(message: 'Something went wrong'));
      }
    } catch (e) {
      emit(const CreateSceneError(message: 'Something went wrong'));
      emit(AddSceneTask(
        tasksList: tasksList,
        automationTasksList: automationTasksList,
        condition: conditionRule,
      ));
    }
  }

  FutureOr<void> _clearTaskList(
      ClearTaskListEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    automationTasksList.clear();
    tasksList.clear();
    emit(AddSceneTask(
      tasksList: tasksList,
      automationTasksList: automationTasksList,
      condition: conditionRule,
    ));
  }

  FutureOr<void> _clearTabToRunSetting(
      ClearTabToRunSetting event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    selectedIcon = '';
    showInDeviceScreen = false;
    emit(AddSceneTask(
        tasksList: tasksList,
        automationTasksList: automationTasksList,
        condition: conditionRule,
        showInDevice: showInDeviceScreen,
        selectedIcon: selectedIcon,
        iconModels: iconModelList));
  }

  FutureOr<void> _fetchSceneTasks(
      FetchSceneTasksEvent event, Emitter<CreateSceneState> emit) async {
    emit(CreateSceneLoading());

    try {
      Project? project = HomeCubit.getInstance().project;

      tasksList.clear();
      tempTasksList.clear();
      selectedValues.clear();
      automationTasksList.clear();
      automationTempTasksList.clear();
      automationSelectedValues.clear();
      automationComparatorValues.clear();
      selectedIcon = '';
      showInDeviceScreen = false;
      effectiveTime =
          EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
      sceneType = CreateSceneEnum.none;
      conditionRule = 'or';

      final response = event.isAutomation
          ? await SceneApi.getAutomationDetails(
              event.sceneId, project?.uuid ?? '')
          : await SceneApi.getSceneDetails(event.sceneId);
      if (response.id.isNotEmpty) {
        if (event.isAutomation) {
          automationTasksList = List<SceneStaticFunction>.from(
              getTaskListFunctionsFromApi(
                  actions: [],
                  isAutomation: true,
                  conditions: response.conditions));
          tasksList = List<SceneStaticFunction>.from(
              getTaskListFunctionsFromApi(
                  actions: response.actions, isAutomation: false));

          conditionRule = response.decisionExpr ?? conditionRule;

          effectiveTime = response.effectiveTime != null
              ? EffectiveTime(
                  start: response.effectiveTime!.start,
                  end: response.effectiveTime!.end,
                  loops: response.effectiveTime!.loops,
                )
              : EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');

          // Set the days directly from the API response
          BlocProvider.of<EffectPeriodBloc>(
                  NavigationService.navigatorKey.currentContext!)
              .add(SetDays(response.effectiveTime?.loops ?? '1111111'));

          // Set Custom Time and reset days first
          BlocProvider.of<EffectPeriodBloc>(
                  NavigationService.navigatorKey.currentContext!)
              .add(SetCustomTime(effectiveTime!.start, effectiveTime!.end));

          emit(AddSceneTask(
              automationTasksList: automationTasksList,
              tasksList: tasksList,
              condition: conditionRule,
              iconModels: iconModelList,
              selectedIcon: selectedIcon,
              showInDevice: showInDeviceScreen));
        } else {
          tasksList = List<SceneStaticFunction>.from(
              getTaskListFunctionsFromApi(
                  actions: response.actions, isAutomation: false));
          selectedIcon = response.icon!;
          showInDeviceScreen = response.showInDevice!;
          emit(AddSceneTask(
              tasksList: tasksList,
              condition: conditionRule,
              iconModels: iconModelList,
              selectedIcon: selectedIcon,
              showInDevice: showInDeviceScreen));
        }
      } else {
        emit(const CreateSceneError(message: 'Something went wrong'));
      }
    } catch (e) {
      emit(const CreateSceneError(message: 'Something went wrong'));
    }
  }

  FutureOr<void> _fetchIconScene(
      SceneIconEvent event, Emitter<CreateSceneState> emit) async {
    emit(CreateSceneLoading());
    try {
      iconModelList = await SceneApi.getIcon();
      emit(AddSceneTask(
          tasksList: tasksList,
          automationTasksList: automationTasksList,
          condition: conditionRule,
          showInDevice: showInDeviceScreen,
          selectedIcon: selectedIcon,
          iconModels: iconModelList));
    } catch (e) {
      emit(const CreateSceneError(message: 'Something went wrong'));
    }
  }

  FutureOr<void> _iconSelected(
      IconSelected event, Emitter<CreateSceneState> emit) async {
    try {
      if (event.confirmSelection) {
        selectedIcon = event.iconId;
      }
      emit(CreateSceneLoading());
      emit(AddSceneTask(
          tasksList: tasksList,
          automationTasksList: automationTasksList,
          showInDevice: showInDeviceScreen,
          condition: conditionRule,
          selectedIcon: event.iconId,
          iconModels: iconModelList));
    } catch (e) {
      emit(const CreateSceneError(message: 'Something went wrong'));
    }
  }

  FutureOr<void> _showInDeviceClicked(
      ShowOnDeviceClicked event, Emitter<CreateSceneState> emit) async {
    try {
      emit(CreateSceneLoading());
      showInDeviceScreen = event.value;
      emit(AddSceneTask(
          tasksList: tasksList,
          automationTasksList: automationTasksList,
          condition: conditionRule,
          selectedIcon: selectedIcon,
          iconModels: iconModelList,
          showInDevice: showInDeviceScreen));
    } catch (e) {
      emit(const CreateSceneError(message: 'Something went wrong'));
    }
  }

  String _getDayFromIndex(int index) {
    const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return days[index];
  }

  FutureOr<void> _clearTempTaskList(
      ClearTempTaskListEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    if (event.isAutomation == true) {
      automationTempTasksList.clear();
      automationSelectedValues.clear();
      automationComparatorValues.clear();
    } else {
      tempTasksList.clear();
      selectedValues.clear();
    }
    emit(AddSceneTask(
      tasksList: tasksList,
      automationTasksList: automationTasksList,
      condition: conditionRule,
    ));
  }

  FutureOr<void> _removeFromSelectedValueById(
      RemoveFromSelectedValueById event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    if (event.isAutomation == true) {
      if (automationSelectedValues.containsKey(event.code)) {
        automationSelectedValues.remove(event.code);
        automationComparatorValues.remove(event.code);
        emit(const SelectedTaskValueState(value: null));
        emit(AddSceneTask(
          tasksList: tasksList,
          automationTasksList: automationTasksList,
          condition: conditionRule,
        ));
      }
    } else {
      if (selectedValues.containsKey(event.code)) {
        selectedValues.remove(event.code);
        emit(const SelectedTaskValueState(value: null));
        emit(AddSceneTask(
          tasksList: tasksList,
          automationTasksList: automationTasksList,
          condition: conditionRule,
        ));
      }
    }
  }

  FutureOr<void> _deleteScene(
      DeleteSceneEvent event, Emitter<CreateSceneState> emit) async {
    emit(DeleteSceneLoading());

    try {
      Project? project = HomeCubit.getInstance().project;

      final response =
          sceneType.name == CreateSceneEnum.deviceStatusChanges.name
              ? await SceneApi.deleteAutomation(
                  automationId: event.sceneId,
                  unitUuid: event.unitUuid,
                  projectId: project?.uuid ?? '')
              : await SceneApi.deleteScene(
                  sceneId: event.sceneId,
                );
      if (response == true) {
        emit(const DeleteSceneSuccess(true));
      } else {
        emit(const DeleteSceneError(message: 'Something went wrong'));
      }
    } catch (e) {
      emit(const DeleteSceneError(message: 'Something went wrong'));
    }
  }

  FutureOr<void> _updateTaskValue(
      UpdateTaskEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneLoading());
    if (event.isAutomation == true) {
      for (var i = 0; i < automationTasksList.length; i++) {
        if (automationTasksList[i].uniqueCustomId == event.taskId) {
          automationTasksList[i] = automationTasksList[i].copyWith(
            functionValue: event.newValue,
          );
          break;
        }
      }
      for (var i = 0; i < tasksList.length; i++) {
        if (tasksList[i].uniqueCustomId == event.taskId) {
          tasksList[i] = tasksList[i].copyWith(
            functionValue: event.newValue,
          );
          break;
        }
      }
    } else {
      for (var i = 0; i < tasksList.length; i++) {
        if (tasksList[i].uniqueCustomId == event.taskId) {
          tasksList[i] = tasksList[i].copyWith(
            functionValue: event.newValue,
          );
          break;
        }
      }
    }
    emit(AddSceneTask(
      tasksList: tasksList,
      automationTasksList: automationTasksList,
      condition: conditionRule,
    ));
  }

  FutureOr<void> _selectConditionRule(
      SelectConditionEvent event, Emitter<CreateSceneState> emit) {
    emit(CreateSceneInitial());
    if (event.condition.contains('any')) {
      conditionRule = 'or';
    } else {
      conditionRule = 'and';
    }

    emit(AddSceneTask(
      tasksList: tasksList,
      automationTasksList: automationTasksList,
      condition: conditionRule,
    ));
  }

  FutureOr<void> _sceneTypeEvent(
      SceneTypeEvent event, Emitter<CreateSceneState> emit) {
    // emit(CreateSceneInitial());

    if (event.type == CreateSceneEnum.tabToRun) {
      sceneType = CreateSceneEnum.tabToRun;
    } else if (event.type == CreateSceneEnum.deviceStatusChanges) {
      sceneType = CreateSceneEnum.deviceStatusChanges;
    } else {
      sceneType = CreateSceneEnum.none;
    }

    // emit(SceneTypeState(event.type));
  }

  FutureOr<void> _onEffectiveTimeEvent(
      EffectiveTimePeriodEvent event, Emitter<CreateSceneState> emit) {
    effectiveTime = event.period;
  }
}
