import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.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/ac/bloc/ac_event.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';

class AcBloc extends Bloc<AcsEvent, AcsState> {
  late AcStatusModel deviceStatus;
  final String deviceId;
  Timer? _timer;
  Timer? _countdownTimer;

  AcBloc({required this.deviceId}) : super(AcsInitialState()) {
    on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
    on<AcFetchBatchStatusEvent>(_onFetchAcBatchStatus);
    on<AcControlEvent>(_onAcControl);
    on<AcBatchControlEvent>(_onAcBatchControl);
    on<AcFactoryResetEvent>(_onFactoryReset);
    on<AcStatusUpdated>(_onAcStatusUpdated);
    on<OnClose>(_onClose);
    on<IncreaseTimeEvent>(_handleIncreaseTime);
    on<DecreaseTimeEvent>(_handleDecreaseTime);
    on<UpdateTimerEvent>(_handleUpdateTimer);
    on<ToggleScheduleEvent>(_handleToggleTimer);
    on<ApiCountdownValueEvent>(_handleApiCountdownValue);
  }
  bool timerActive = false;
  int scheduledHours = 0;
  int scheduledMinutes = 0;

  FutureOr<void> _onFetchAcStatus(
      AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    try {
      final status =
          await DevicesManagementApi().getDeviceStatus(event.deviceId);
      deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
      if (deviceStatus.countdown1 != 0) {
        // Convert API value to minutes
        final totalMinutes = deviceStatus.countdown1 * 6;
        scheduledHours = totalMinutes ~/ 60;
        scheduledMinutes = totalMinutes % 60;
        timerActive = true;
        _startCountdownTimer(emit);
      }

      emit(ACStatusLoaded(
        status: deviceStatus,
        scheduledHours: scheduledHours,
        scheduledMinutes: scheduledMinutes,
        isTimerActive: timerActive,
      ));

      _listenToChanges(event.deviceId);
    } catch (e) {
      emit(AcsFailedState(error: e.toString()));
    }
  }

  _listenToChanges(deviceId) {
    try {
      DatabaseReference ref =
          FirebaseDatabase.instance.ref('device-status/$deviceId');
      Stream<DatabaseEvent> stream = ref.onValue;

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

        if (_timer != null) {
          await Future.delayed(const Duration(seconds: 1));
        }
        Map<dynamic, dynamic> usersMap =
            event.snapshot.value as Map<dynamic, dynamic>;

        List<Status> statusList = [];

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

        deviceStatus =
            AcStatusModel.fromJson(usersMap['productUuid'], statusList);
        if (!isClosed) {
          add(AcStatusUpdated(deviceStatus));
        }
      });
    } catch (_) {}
  }

  void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
    deviceStatus = event.deviceStatus;
    emit(ACStatusLoaded(status: deviceStatus));
  }

  FutureOr<void> _onAcControl(
      AcControlEvent event, Emitter<AcsState> emit) async {
    final oldValue = _getValueByCode(event.code);

    _updateLocalValue(event.code, event.value, emit);

    emit(ACStatusLoaded(status: deviceStatus));

    await _runDebounce(
      isBatch: false,
      deviceId: event.deviceId,
      code: event.code,
      value: event.value,
      oldValue: oldValue,
      emit: emit,
    );
  }

  Future<void> _runDebounce({
    required dynamic deviceId,
    required String code,
    required dynamic value,
    required dynamic oldValue,
    required Emitter<AcsState> emit,
    required bool isBatch,
  }) async {
    late String id;

    if (deviceId is List) {
      id = deviceId.first;
    } else {
      id = deviceId;
    }

    if (_timer != null) {
      _timer!.cancel();
    }
    _timer = Timer(const Duration(seconds: 1), () async {
      try {
        late bool response;
        if (isBatch) {
          response = await DevicesManagementApi()
              .deviceBatchControl(deviceId, code, value);
        } else {
          response = await DevicesManagementApi()
              .deviceControl(deviceId, Status(code: code, value: value));
        }

        if (!response) {
          _revertValueAndEmit(id, code, oldValue, emit);
        }
      } catch (e) {
        if (e is DioException && e.response != null) {
          debugPrint('Error response: ${e.response?.data}');
        }
        _revertValueAndEmit(id, code, oldValue, emit);
      }
    });
  }

  void _revertValueAndEmit(
      String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
    _updateLocalValue(code, oldValue, emit);
    emit(ACStatusLoaded(status: deviceStatus));
  }

  void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
    switch (code) {
      case 'switch':
        if (value is bool) {
          deviceStatus = deviceStatus.copyWith(acSwitch: value);
        }
        break;
      case 'temp_set':
        if (value is int) {
          deviceStatus = deviceStatus.copyWith(tempSet: value);
        }
        break;
      case 'mode':
        if (value is String) {
          deviceStatus = deviceStatus.copyWith(
            modeString: value,
          );
        }
        break;
      case 'level':
        if (value is String) {
          deviceStatus = deviceStatus.copyWith(
            fanSpeedsString: value,
          );
        }
        break;
      case 'child_lock':
        if (value is bool) {
          deviceStatus = deviceStatus.copyWith(childLock: value);
        }

      case 'countdown_time':
        if (value is int) {
          deviceStatus = deviceStatus.copyWith(countdown1: value);
        }
        break;
      default:
        break;
    }
    emit(ACStatusLoaded(status: deviceStatus));
  }

  dynamic _getValueByCode(String code) {
    switch (code) {
      case 'switch':
        return deviceStatus.acSwitch;
      case 'temp_set':
        return deviceStatus.tempSet;
      case 'mode':
        return deviceStatus.modeString;
      case 'level':
        return deviceStatus.fanSpeedsString;
      case 'child_lock':
        return deviceStatus.childLock;
      case 'countdown_time':
        return deviceStatus.countdown1;
      default:
        return null;
    }
  }

  FutureOr<void> _onFetchAcBatchStatus(
      AcFetchBatchStatusEvent event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    try {
      final status =
          await DevicesManagementApi().getBatchStatus(event.devicesIds);
      deviceStatus =
          AcStatusModel.fromJson(event.devicesIds.first, status.status);
      emit(ACStatusLoaded(status: deviceStatus));
    } catch (e) {
      emit(AcsFailedState(error: e.toString()));
    }
  }

  FutureOr<void> _onAcBatchControl(
      AcBatchControlEvent event, Emitter<AcsState> emit) async {
    final oldValue = _getValueByCode(event.code);

    _updateLocalValue(event.code, event.value, emit);

    emit(ACStatusLoaded(status: deviceStatus));

    await _runDebounce(
      isBatch: true,
      deviceId: event.devicesIds,
      code: event.code,
      value: event.value,
      oldValue: oldValue,
      emit: emit,
    );
  }

  FutureOr<void> _onFactoryReset(
      AcFactoryResetEvent event, Emitter<AcsState> emit) async {
    emit(AcsLoadingState());
    try {
      final response = await DevicesManagementApi().factoryReset(
        event.factoryResetModel,
        event.deviceId,
      );
      if (!response) {
        emit(const AcsFailedState(error: 'Failed'));
      } else {
        add(AcFetchDeviceStatusEvent(event.deviceId));
      }
    } catch (e) {
      emit(AcsFailedState(error: e.toString()));
    }
  }

  void _onClose(OnClose event, Emitter<AcsState> emit) {
    _countdownTimer?.cancel();
    _timer?.cancel();
  }

  void _handleIncreaseTime(IncreaseTimeEvent event, Emitter<AcsState> emit) {
    if (state is! ACStatusLoaded) return;
    final currentState = state as ACStatusLoaded;
    int newHours = scheduledHours;
    int newMinutes = scheduledMinutes + 30;
    newHours += newMinutes ~/ 60;
    newMinutes = newMinutes % 60;
    if (newHours > 23) {
      newHours = 23;
      newMinutes = 59;
    }
    scheduledHours = newHours;
    scheduledMinutes = newMinutes;

    emit(currentState.copyWith(
      scheduledHours: scheduledHours,
      scheduledMinutes: scheduledMinutes,
    ));
  }

  void _handleDecreaseTime(DecreaseTimeEvent event, Emitter<AcsState> emit) {
    if (state is! ACStatusLoaded) return;
    final currentState = state as ACStatusLoaded;
    int totalMinutes = (scheduledHours * 60) + scheduledMinutes;
    totalMinutes = (totalMinutes - 30).clamp(0, 1440);
    scheduledHours = totalMinutes ~/ 60;
    scheduledMinutes = totalMinutes % 60;

    emit(currentState.copyWith(
      scheduledHours: scheduledHours,
      scheduledMinutes: scheduledMinutes,
    ));
  }

  Future<void> _handleToggleTimer(
      ToggleScheduleEvent event, Emitter<AcsState> emit) async {
    if (state is! ACStatusLoaded) return;
    final currentState = state as ACStatusLoaded;

    timerActive = !timerActive;

    if (timerActive) {
      final totalMinutes = scheduledHours * 60 + scheduledMinutes;
      if (totalMinutes <= 0) {
        timerActive = false;
        emit(currentState.copyWith(isTimerActive: timerActive));
        return;
      }

      try {
        final scaledValue = totalMinutes ~/ 6;
        await _runDebounce(
          isBatch: false,
          deviceId: deviceId,
          code: 'countdown_time',
          value: scaledValue,
          oldValue: scaledValue,
          emit: emit,
        );
        _startCountdownTimer(emit);
        emit(currentState.copyWith(isTimerActive: timerActive));
      } catch (e) {
        timerActive = false;
        emit(AcsFailedState(error: e.toString()));
      }
    } else {
      await _runDebounce(
        isBatch: false,
        deviceId: deviceId,
        code: 'countdown_time',
        value: 0,
        oldValue: 0,
        emit: emit,
      );
      _countdownTimer?.cancel();
      scheduledHours = 0;
      scheduledMinutes = 0;
      emit(currentState.copyWith(
        isTimerActive: timerActive,
        scheduledHours: 0,
        scheduledMinutes: 0,
      ));
    }
  }

  void _startCountdownTimer(Emitter<AcsState> emit) {
    _countdownTimer?.cancel();
    int totalSeconds = (scheduledHours * 3600) + (scheduledMinutes * 60);

    _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (totalSeconds > 0) {
        totalSeconds--;
        scheduledHours = totalSeconds ~/ 3600;
        scheduledMinutes = (totalSeconds % 3600) ~/ 60;
        add(UpdateTimerEvent());
      } else {
        _countdownTimer?.cancel();
        timerActive = false;
        scheduledHours = 0;
        scheduledMinutes = 0;
        add(TimerCompletedEvent());
      }
    });
  }

  void _handleUpdateTimer(UpdateTimerEvent event, Emitter<AcsState> emit) {
    if (state is ACStatusLoaded) {
      final currentState = state as ACStatusLoaded;
      emit(currentState.copyWith(
        scheduledHours: scheduledHours,
        scheduledMinutes: scheduledMinutes,
        isTimerActive: timerActive,
      ));
    }
  }

  void _handleApiCountdownValue(
      ApiCountdownValueEvent event, Emitter<AcsState> emit) {
    if (state is ACStatusLoaded) {
      final totalMinutes = event.apiValue * 6;
      final scheduledHours = totalMinutes ~/ 60;
      scheduledMinutes = totalMinutes % 60;
      _startCountdownTimer(
        emit,
      );
      add(UpdateTimerEvent());
    }
  }

  @override
  Future<void> close() {
    add(OnClose());
    return super.close();
  }
}
