import 'dart:async';
import 'dart:math';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/model/offline_password_model.dart';
import 'package:syncrow_app/features/devices/model/offline_temporary_password.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/model/create_temporary_password_model.dart';
import 'package:syncrow_app/features/devices/model/temporary_password_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/hour_picker_dialog.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';

class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
  final String deviceId;
  late SmartDoorModel deviceStatus;
  bool isSavingPassword = false;

  SmartDoorBloc({required this.deviceId}) : super(InitialState()) {
    on<InitialEvent>(_fetchSmartDoorStatus);
    on<DoorLockUpdated>(_doorLockUpdated);
    on<InitialPasswordsPage>(getTemporaryPasswords);
    on<InitialOneTimePassword>(getOneTimePasswords);
    on<InitialTimeLimitPassword>(getTimeLimitPasswords);
    on<UpdateLockEvent>(_updateLock);
    on<SavePasswordEvent>(savePassword);
    on<ToggleRepeatEvent>(toggleRepeat);
    on<SetStartEndTimeEvent>(setStartEndTime);
    on<ChangeTimeEvent>(changeTime);
    on<GeneratePasswordEvent>(generate7DigitNumber);
    on<SelectTimeEvent>(selectTimeOfLinePassword);
    on<SelectTimeOnlinePasswordEvent>(selectTimeOnlinePassword);
    on<DeletePasswordEvent>(deletePassword);
    on<GenerateAndSavePasswordTimeLimitEvent>(
        generateAndSavePasswordTimeLimited);
    on<GenerateAndSavePasswordOneTimeEvent>(generateAndSavePasswordOneTime);
    on<ToggleDaySelectionEvent>(toggleDaySelection);
    on<RenamePasswordEvent>(_renamePassword);
  }

  TextEditingController passwordController = TextEditingController();
  TextEditingController passwordNameController = TextEditingController();
  String effectiveTime = 'Select Time';
  String passwordId = '';
  int? effectiveTimeTimeStamp;
  String expirationTime = 'Select Time';
  int? expirationTimeTimeStamp;
  bool repeat = false;
  bool isStartEndTime = true;
  DateTime? startTime;
  DateTime? endTime;
  int unlockRequest = 0;
  List<TemporaryPassword>? temporaryPasswords = [];
  List<OfflinePasswordModel>? oneTimePasswords = [];
  List<OfflinePasswordModel>? timeLimitPasswords = [];

  Future generate7DigitNumber(
      GeneratePasswordEvent event, Emitter<SmartDoorState> emit) async {
    emit(LoadingInitialState());
    passwordController.clear();
    Random random = Random();
    int min = 1000000;
    int max = 9999999;
    passwordController.text = (min + random.nextInt(max - min + 1)).toString();
    emit(GeneratePasswordState());
    return passwordController.text;
  }

  Future generateAndSavePasswordOneTime(
      GenerateAndSavePasswordOneTimeEvent event,
      Emitter<SmartDoorState> emit) async {
    try {
      if (isSavingPassword) return;
      isSavingPassword = true;
      emit(LoadingInitialState());
      var res = await DevicesAPI.generateOneTimePassword(deviceId: deviceId);
      ApiResponse pass = ApiResponse.fromJson(res);
      passwordController.text = pass.data.offlineTempPassword;
      passwordId = pass.data.offlineTempPasswordId;

      Future.delayed(const Duration(seconds: 1), () {
        Clipboard.setData(ClipboardData(text: passwordController.text));
      });
      emit(const GeneratePasswordOneTimestate(generated: true));
    } catch (_) {
      emit(FailedState(errorMessage: _.toString()));
    } finally {
      isSavingPassword = false;
    }
  }

  void _fetchSmartDoorStatus(
      InitialEvent event, Emitter<SmartDoorState> emit) async {
    try {
      emit(LoadingInitialState());
      var response = await DevicesAPI.getDeviceStatus(deviceId);
      List<StatusModel> statusModelList = [];
      for (var status in response['status']) {
        statusModelList.add(StatusModel.fromJson(status));
      }
      deviceStatus = SmartDoorModel.fromJson(statusModelList);
      emit(UpdateState(smartDoorModel: deviceStatus));
      _listenToChanges();
    } catch (e) {
      emit(FailedState(errorMessage: e.toString()));
      return;
    }
  }

  StreamSubscription<DatabaseEvent>? _streamSubscription;
  Timer? _timer;

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

      _streamSubscription = stream.listen((DatabaseEvent event) async {
        if (_timer != null) {
          await Future.delayed(const Duration(seconds: 2));
        }
        Map<dynamic, dynamic> usersMap =
            event.snapshot.value as Map<dynamic, dynamic>;
        List<StatusModel> statusList = [];
        usersMap['status'].forEach((element) {
          statusList
              .add(StatusModel(code: element['code'], value: element['value']));
        });
        deviceStatus = SmartDoorModel.fromJson(statusList);
        if (!isClosed) {
          add(DoorLockUpdated());
        }
      });
    } catch (_) {}
  }

  @override
  Future<void> close() async {
    _streamSubscription?.cancel();
    _streamSubscription = null;
    return super.close();
  }
 

  _doorLockUpdated(DoorLockUpdated event, Emitter<SmartDoorState> emit) {
    unlockRequest = deviceStatus.unlockRequest;
    emit(UpdateState(smartDoorModel: deviceStatus));
  }

  void _renamePassword(
      RenamePasswordEvent event, Emitter<SmartDoorState> emit) async {
    try {
      emit(LoadingInitialState());
      await DevicesAPI.renamePass(
          name: passwordNameController.text,
          doorLockUuid: deviceId,
          passwordId: passwordId);
      add(InitialOneTimePassword());
      add(InitialTimeLimitPassword());
      emit(UpdateState(smartDoorModel: deviceStatus));
    } catch (e) {
      emit(FailedState(errorMessage: e.toString()));
      return;
    }
  }

  void getTemporaryPasswords(
      InitialPasswordsPage event, Emitter<SmartDoorState> emit) async {
    try {
      emit(LoadingInitialState());
      var response = await DevicesAPI.getTemporaryPasswords(
        deviceId,
      );
      if (response is List) {
        temporaryPasswords =
            response.map((item) => TemporaryPassword.fromJson(item)).toList();
      } else if (response is Map && response.containsKey('data')) {
        temporaryPasswords = (response['data'] as List)
            .map((item) => TemporaryPassword.fromJson(item))
            .toList();
      }
      emit(TemporaryPasswordsLoadedState(
          temporaryPassword: temporaryPasswords!));
    } catch (e) {
      emit(FailedState(errorMessage: e.toString()));
    }
  }

  void getOneTimePasswords(
      InitialOneTimePassword event, Emitter<SmartDoorState> emit) async {
    try {
      emit(LoadingInitialState());
      var response = await DevicesAPI.getOneTimePasswords(deviceId);
      if (response is List) {
        oneTimePasswords = response
            .map((item) => OfflinePasswordModel.fromJson(item))
            .toList();
      }
      emit(TemporaryPasswordsLoadedState(
          temporaryPassword: temporaryPasswords!));
    } catch (e) {
      emit(FailedState(errorMessage: e.toString()));
    }
  }

  void getTimeLimitPasswords(
      InitialTimeLimitPassword event, Emitter<SmartDoorState> emit) async {
    try {
      emit(LoadingInitialState());
      var response = await DevicesAPI.getTimeLimitPasswords(deviceId);
      if (response is List) {
        timeLimitPasswords = response
            .map((item) => OfflinePasswordModel.fromJson(item))
            .toList();
      }

      emit(TemporaryPasswordsLoadedState(
          temporaryPassword: temporaryPasswords!));
    } catch (e) {
      emit(FailedState(errorMessage: e.toString()));
    }
  }

  changeTime(ChangeTimeEvent event, Emitter<SmartDoorState> emit) {
    if (event.isStartEndTime == true) {
      startTime = event.val;
    } else {
      endTime = event.val;
    }
  }

  bool toggleRepeat(ToggleRepeatEvent event, Emitter<SmartDoorState> emit) {
    emit(LoadingInitialState());
    repeat = !repeat;
    emit(IsRepeatState(repeat: repeat));
    return repeat;
  }

  bool setStartEndTime(
      SetStartEndTimeEvent event, Emitter<SmartDoorState> emit) {
    emit(LoadingInitialState());
    isStartEndTime = event.val;
    emit(IsStartEndState(isStartEndTime: isStartEndTime));
    return isStartEndTime;
  }

  void _updateLock(UpdateLockEvent event, Emitter<SmartDoorState> emit) async {
    emit(LoadingNewSate(smartDoorModel: deviceStatus));
    try {
      // final response = await DevicesAPI.controlDevice(
      //     DeviceControlModel(deviceId: deviceId, code: 'normal_open_switch', value: !event.value),
      //     deviceId);

      final response = await DevicesAPI.openDoorLock(deviceId);

      if (response) {
        deviceStatus.normalOpenSwitch = !event.value;
      }
    } catch (_) {}
    emit(UpdateState(smartDoorModel: deviceStatus));
  }

  Future<void> selectTimeOfLinePassword(
      SelectTimeEvent event, Emitter<SmartDoorState> emit) async {
    emit(ChangeTimeState());
    final DateTime? picked = await showDatePicker(
      context: event.context,
      initialDate: DateTime.now(),
      firstDate: DateTime.now(),
      lastDate: DateTime(2101),
    );
    if (picked != null) {
      final TimeOfDay? timePicked = await showHourPicker(
        context: event.context,
        initialTime: TimeOfDay.now(),
      );
      if (timePicked != null) {
        final selectedDateTime = DateTime(
          picked.year,
          picked.month,
          picked.day,
          timePicked.hour,
          0,
        );
        final selectedTimestamp = DateTime(
              selectedDateTime.year,
              selectedDateTime.month,
              selectedDateTime.day,
              selectedDateTime.hour,
              selectedDateTime.minute,
            ).millisecondsSinceEpoch ~/
            1000; // Divide by 1000 to remove milliseconds
        if (event.isEffective) {
          if (expirationTimeTimeStamp != null &&
              selectedTimestamp > expirationTimeTimeStamp!) {
            CustomSnackBar.displaySnackBar(
                'Effective Time cannot be later than Expiration Time.');
          } else {
            effectiveTime = selectedDateTime
                .toString()
                .split('.')
                .first; // Remove seconds and milliseconds
            effectiveTimeTimeStamp = selectedTimestamp;
          }
        } else {
          if (effectiveTimeTimeStamp != null &&
              selectedTimestamp < effectiveTimeTimeStamp!) {
            CustomSnackBar.displaySnackBar(
                'Expiration Time cannot be earlier than Effective Time.');
          } else {
            expirationTime = selectedDateTime
                .toString()
                .split('.')
                .first; // Remove seconds and milliseconds
            expirationTimeTimeStamp = selectedTimestamp;
          }
        }
        emit(TimeSelectedState());
      }
    }
  }

  Future<void> selectTimeOnlinePassword(
      SelectTimeOnlinePasswordEvent event, Emitter<SmartDoorState> emit) async {
    emit(ChangeTimeState());
    final DateTime? picked = await showDatePicker(
      context: event.context,
      initialDate: DateTime.now(),
      firstDate: DateTime(2015, 8),
      lastDate: DateTime(2101),
    );
    if (picked != null) {
      final TimeOfDay? timePicked = await showTimePicker(
        context: event.context,
        initialTime: TimeOfDay.now(),
        builder: (context, child) {
          return Theme(
            data: ThemeData.light().copyWith(
              colorScheme: const ColorScheme.light(
                primary: ColorsManager.primaryColor,
                onSurface: Colors.black,
              ),
              buttonTheme: const ButtonThemeData(
                colorScheme: ColorScheme.light(
                  primary: Colors.green,
                ),
              ),
            ),
            child: child!,
          );
        },
      );
      if (timePicked != null) {
        final selectedDateTime = DateTime(
          picked.year,
          picked.month,
          picked.day,
          timePicked.hour,
          timePicked.minute,
        );
        final selectedTimestamp = DateTime(
              selectedDateTime.year,
              selectedDateTime.month,
              selectedDateTime.day,
              selectedDateTime.hour,
              selectedDateTime.minute,
            ).millisecondsSinceEpoch ~/
            1000; // Divide by 1000 to remove milliseconds
        if (event.isEffective) {
          if (expirationTimeTimeStamp != null &&
              selectedTimestamp > expirationTimeTimeStamp!) {
            CustomSnackBar.displaySnackBar(
                'Effective Time cannot be later than Expiration Time.');
          } else {
            effectiveTime = selectedDateTime
                .toString()
                .split('.')
                .first; // Remove seconds and milliseconds
            effectiveTimeTimeStamp = selectedTimestamp;
          }
        } else {
          if (effectiveTimeTimeStamp != null &&
              selectedTimestamp < effectiveTimeTimeStamp!) {
            CustomSnackBar.displaySnackBar(
                'Expiration Time cannot be earlier than Effective Time.');
          } else {
            expirationTime = selectedDateTime
                .toString()
                .split('.')
                .first; // Remove seconds and milliseconds
            expirationTimeTimeStamp = selectedTimestamp;
          }
        }
        emit(TimeSelectedState());
      }
    }
  }

  Future<void> savePassword(
      SavePasswordEvent event, Emitter<SmartDoorState> emit) async {
    if (_validateInputs() || isSavingPassword) return;
    try {
      isSavingPassword = true;
      emit(LoadingSaveState());
      await DevicesAPI.createPassword(
        deviceId: deviceId,
        effectiveTime: effectiveTimeTimeStamp.toString(),
        invalidTime: expirationTimeTimeStamp.toString(),
        name: passwordNameController.text,
        password: passwordController.text,
        scheduleList: [
          if (repeat)
            Schedule(
              effectiveTime: getTimeOnly(startTime),
              invalidTime: getTimeOnly(endTime).toString(),
              workingDay: selectedDays,
            ),
        ],
      );
      Navigator.of(event.context).pop(true);
      CustomSnackBar.displaySnackBar('Save Successfully');
      emit(SaveState());
    } catch (_) {
    } finally {
      isSavingPassword = false;
    }
  }

  Future<void> generateAndSavePasswordTimeLimited(
      GenerateAndSavePasswordTimeLimitEvent event,
      Emitter<SmartDoorState> emit) async {
    if (timeLimitValidate() || isSavingPassword) return;
    try {
      isSavingPassword = true;
      emit(LoadingInitialState());
      var res = await DevicesAPI.generateMultiTimePassword(
        deviceId: deviceId,
        effectiveTime: effectiveTimeTimeStamp.toString(),
        invalidTime: expirationTimeTimeStamp.toString(),
      );
      ApiResponse pass = ApiResponse.fromJson(res);
      passwordController.text = pass.data.offlineTempPassword;
      passwordId = pass.data.offlineTempPasswordId;
      CustomSnackBar.displaySnackBar('Save Successfully');
      add(InitialTimeLimitPassword());
      Future.delayed(const Duration(seconds: 1), () {
        Clipboard.setData(ClipboardData(text: passwordController.text));
      });
      emit(const GeneratePasswordOneTimestate(generated: true));
    } catch (_) {
      add(InitialPasswordsPage());
    } finally {
      isSavingPassword = false;
    }
  }

  Future<void> deletePassword(
      DeletePasswordEvent event, Emitter<SmartDoorState> emit) async {
    try {
      emit(LoadingInitialState());
      await DevicesAPI.deletePassword(
              deviceId: deviceId, passwordId: event.passwordId)
          .then((value) async {
        add(InitialPasswordsPage());
      });
    } catch (e) {
      emit(FailedState(errorMessage: e.toString()));
    }
  }

  bool _validateInputs() {
    if (passwordController.text.length < 7) {
      CustomSnackBar.displaySnackBar('Password less than 7');
      return true;
    }

    if (passwordController.text.isEmpty) {
      CustomSnackBar.displaySnackBar('Password required');
      return true;
    }
    if (passwordNameController.text.isEmpty) {
      CustomSnackBar.displaySnackBar('Password name required');
      return true;
    }

    if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
      CustomSnackBar.displaySnackBar('Select effective time');
      return true;
    }

    if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) {
      CustomSnackBar.displaySnackBar('Select expiration time');
      return true;
    }

    if (repeat == true && (endTime == null || startTime == null)) {
      CustomSnackBar.displaySnackBar(
          'Start Time and End time and the days required ');
      return true;
    }
    return false;
  }

  bool timeLimitValidate() {
    if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
      CustomSnackBar.displaySnackBar('Select effective time');
      return true;
    }
    if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) {
      CustomSnackBar.displaySnackBar('Select expiration time');
      return true;
    }
    return false;
  }

  List<Map<String, String>> days = [
    {"day": "Sun", "key": "Sun"},
    {"day": "Mon", "key": "Mon"},
    {"day": "Tue", "key": "Tue"},
    {"day": "Wed", "key": "Wed"},
    {"day": "Thu", "key": "Thu"},
    {"day": "Fri", "key": "Fri"},
    {"day": "Sat", "key": "Sat"},
  ];

  List<String> selectedDays = [];

  Future<void> toggleDaySelection(
    ToggleDaySelectionEvent event,
    Emitter<SmartDoorState> emit,
  ) async {
    emit(LoadingInitialState());
    if (selectedDays.contains(event.key)) {
      selectedDays.remove(event.key);
    } else {
      selectedDays.add(event.key);
    }
    emit(ChangeTimeState());
  }

  String getTimeOnly(DateTime? dateTime) {
    if (dateTime == null) return '';
    return DateFormat('HH:mm').format(dateTime);
  }
}
