import 'dart:math';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/custom_dialog.dart';
import 'package:syncrow_web/pages/common/hour_picker_dialog.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
import 'package:syncrow_web/pages/visitor_password/model/failed_operation.dart';
import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart';
import 'package:syncrow_web/services/access_mang_api.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_web/utils/snack_bar.dart';

class VisitorPasswordBloc
    extends Bloc<VisitorPasswordEvent, VisitorPasswordState> {

  VisitorPasswordBloc() : super(VisitorPasswordInitial()) {
    on<SelectUsageFrequency>(selectUsageFrequency);
    on<FetchDevice>(_onFetchDevice);
    on<SelectPasswordType>(selectAccessType);
    on<SelectTimeVisitorPassword>(selectTimeVisitorPassword);
    on<ToggleRepeatEvent>(toggleRepeat);
    on<ToggleDaySelectionEvent>(toggleDaySelection);
    on<SelectDeviceEvent>(selectDevice);
    on<UpdateFilteredDevicesEvent>(_onUpdateFilteredDevices);
    on<OnlineOneTimePasswordEvent>(postOnlineOneTimePassword);
    on<OnlineMultipleTimePasswordEvent>(postOnlineMultipleTimePassword);
    on<OfflineMultipleTimePasswordEvent>(postOfflineMultipleTimePassword);
    on<OfflineOneTimePasswordEvent>(postOfflineOneTimePassword);
    on<SelectTimeEvent>(selectTimeOfLinePassword);
    on<ChangeTimeEvent>(changeTime);
  }

  final TextEditingController userNameController = TextEditingController();
  final TextEditingController emailController = TextEditingController();
  final TextEditingController deviceNameController = TextEditingController();
  final TextEditingController deviceIdController = TextEditingController();
  final TextEditingController unitNameController = TextEditingController();
  final TextEditingController virtualAddressController =
      TextEditingController();
  List<String> selectedDevices = [];

  List<DeviceModel> data = [];
  List<String> selectedDeviceIds = [];
  String effectiveTime = 'Start Time';
  String expirationTime = 'End Time';

  final forgetFormKey = GlobalKey<FormState>();

  String accessTypeSelected = 'Online Password';
  String usageFrequencySelected = 'One-Time';
  String passwordController = '';
  String accessPeriodValidate = '';

  bool repeat = false;

  int? effectiveTimeTimeStamp;
  int? expirationTimeTimeStamp;

  DateTime? startTime = DateTime.now();
  DateTime? endTime;

  String startTimeAccess = DateTime.now().toString().split('.').first;
  String endTimeAccess = 'End Time';
  PasswordStatus? passwordStatus;
  selectAccessType(
      SelectPasswordType event, Emitter<VisitorPasswordState> emit) {
    accessTypeSelected = event.type;
    emit(PasswordTypeSelected(event.type));
  }

  selectUsageFrequency(
      SelectUsageFrequency event, Emitter<VisitorPasswordState> emit) {
    usageFrequencySelected = event.usageType;
    emit(UsageFrequencySelected(event.usageType));
  }

  Future<void> selectTimeVisitorPassword(
    SelectTimeVisitorPassword event,
    Emitter<VisitorPasswordState> emit,
  ) async {
    final DateTime? picked = await showDatePicker(
      context: event.context,
      initialDate: DateTime.now(),
      firstDate: DateTime(2015, 8),
      lastDate: DateTime.now().add(const Duration(days: 5095)),
    );

    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 =
            selectedDateTime.millisecondsSinceEpoch ~/ 1000;

        if (event.isStart) {
          if (expirationTimeTimeStamp != null &&
              selectedTimestamp > expirationTimeTimeStamp!) {
            CustomSnackBar.displaySnackBar(
              'Effective Time cannot be later than Expiration Time.',
            );
            return;
          }
          if(selectedTimestamp < DateTime.now().millisecondsSinceEpoch ~/ 1000) {
            if(selectedTimestamp < DateTime.now().millisecondsSinceEpoch ~/ 1000) {
              await showDialog<void>(
                context: event.context,
                builder: (context) => AlertDialog(
                  title: const Text('Effective Time cannot be earlier than current time.'),
                  actionsAlignment: MainAxisAlignment.center,
                  content: 
                    FilledButton(
                      onPressed: () {
                        Navigator.of(event.context).pop();
                        add(SelectTimeVisitorPassword(context: event.context, isStart: true, isRepeat: false));
                      },
                      child: const Text('OK'),
                    ),
                  
                ),
              );
            }
            return;
          }
          effectiveTimeTimeStamp = selectedTimestamp;
          startTimeAccess = selectedDateTime.toString().split('.').first;
        } else {
          if (effectiveTimeTimeStamp != null &&
              selectedTimestamp < effectiveTimeTimeStamp!) {
            CustomSnackBar.displaySnackBar(
              'Expiration Time cannot be earlier than Effective Time.',
            );
            return;
          }
          expirationTimeTimeStamp = selectedTimestamp;
          endTimeAccess = selectedDateTime.toString().split('.').first;
        }
        emit(ChangeTimeState());
        emit(VisitorPasswordInitial());
      }
    }
  }

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

  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<VisitorPasswordState> emit,
  ) async {
    emit(LoadingInitialState());
    if (selectedDays.contains(event.key)) {
      selectedDays.remove(event.key);
    } else {
      selectedDays.add(event.key);
    }
    emit(ChangeTimeState());
  }

  Future<void> _onFetchDevice(
      FetchDevice event, Emitter<VisitorPasswordState> emit) async {
    try {
      emit(DeviceLoaded());
         final projectUuid = await ProjectManager.getProjectUUID() ?? '';

      data = await AccessMangApi().fetchDoorLockDeviceList(projectUuid);
      emit(TableLoaded(data));
    } catch (e) {
      print("error: $e");
      emit(FailedState(e.toString()));
    }
  }

  //online password
  Future<void> postOnlineOneTimePassword(OnlineOneTimePasswordEvent event,
      Emitter<VisitorPasswordState> emit) async {
    try {
      emit(LoadingInitialState());
      generate7DigitNumber();
      var res = await AccessMangApi().postOnlineOneTime(
          email: event.email,
          password: passwordController,
          devicesUuid: selectedDevices,
          passwordName: event.passwordName,
          effectiveTime: effectiveTimeTimeStamp.toString(),
          invalidTime: expirationTimeTimeStamp.toString());
      if (res['statusCode'] == 201) {
        passwordStatus = PasswordStatus.fromJson(res['data']);
        emit(SuccessState());
      }
      emit(TableLoaded(data));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(FailedState(errorMessage.toString()));
    }
  }

  Future<void> postOnlineMultipleTimePassword(
      OnlineMultipleTimePasswordEvent event,
      Emitter<VisitorPasswordState> emit) async {
    try {
      emit(LoadingInitialState());

      await generate7DigitNumber();
      var res = await AccessMangApi().postOnlineMultipleTime(
          scheduleList: [
            if (repeat)
              Schedule(
                effectiveTime: getTimeFromDateTimeString(effectiveTime),
                invalidTime:
                    getTimeFromDateTimeString(expirationTime).toString(),
                workingDay: selectedDays,
              ),
          ],
          password: passwordController,
          invalidTime: expirationTimeTimeStamp.toString(),
          effectiveTime: effectiveTimeTimeStamp.toString(),
          email: event.email,
          devicesUuid: selectedDevices,
          passwordName: event.passwordName);
      if (res['statusCode'] == 201) {
        passwordStatus = PasswordStatus.fromJson(res['data']);
        emit(SuccessState());
      }
      emit(TableLoaded(data));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(FailedState(errorMessage.toString()));
    }
  }

  //offline password
  Future<void> postOfflineOneTimePassword(OfflineOneTimePasswordEvent event,
      Emitter<VisitorPasswordState> emit) async {
    try {
      emit(LoadingInitialState());
      await generate7DigitNumber();
      var res = await AccessMangApi().postOffLineOneTime(
          email: event.email,
          devicesUuid: selectedDevices,
          passwordName: event.passwordName);
      if (res['statusCode'] == 201) {
        passwordStatus = PasswordStatus.fromJson(res['data']);
        emit(SuccessState());
      }
      emit(TableLoaded(data));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(FailedState(errorMessage.toString()));
    }
  }

  Future<void> postOfflineMultipleTimePassword(
      OfflineMultipleTimePasswordEvent event,
      Emitter<VisitorPasswordState> emit) async {
    try {
      emit(LoadingInitialState());
      await generate7DigitNumber();
      var res = await AccessMangApi().postOffLineMultipleTime(
        email: event.email,
        devicesUuid: selectedDevices,
        passwordName: event.passwordName,
        invalidTime: expirationTimeTimeStamp.toString(),
        effectiveTime: effectiveTimeTimeStamp.toString(),
      );
      if (res['statusCode'] == 201) {
        passwordStatus = PasswordStatus.fromJson(res['data']);
        emit(SuccessState());
      }
      emit(TableLoaded(data));
    } on DioException catch (e) {
      final errorData = e.response!.data;
      String errorMessage = errorData['message'];
      emit(FailedState(errorMessage.toString()));
    }
  }

  void selectDevice(
      SelectDeviceEvent event, Emitter<VisitorPasswordState> emit) {
    if (selectedDeviceIds.contains(event.deviceId)) {
      selectedDeviceIds.remove(event.deviceId);
    } else {
      selectedDeviceIds.add(event.deviceId);
    }
  }

  String? validate(String? value) {
    if (value == null || value.isEmpty) {
      return '';
    }
    return null;
  }

  Future generate7DigitNumber() async {
    passwordController = '';
    Random random = Random();
    int min = 1000000;
    int max = 9999999;
    passwordController = (min + random.nextInt(max - min + 1)).toString();
    return passwordController;
  }

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

  void filterDevices() {
    final deviceName = deviceNameController.text.toLowerCase();
    final deviceId = deviceIdController.text.toLowerCase();
    // final unitName = unitNameController.text.toLowerCase();
    final filteredData = data.where((device) {
      final matchesDeviceName = device.name.toLowerCase().contains(deviceName);
      final matchesDeviceId = device.uuid.toLowerCase().contains(deviceId);
      // final matchesUnitName = device.unitName.toLowerCase().contains(unitName); // Assuming unitName is a property of the device
      return matchesDeviceName && matchesDeviceId;
    }).toList();
    add(UpdateFilteredDevicesEvent(filteredData));
  }

  Stream<VisitorPasswordState> mapEventToState(
      VisitorPasswordEvent event) async* {
    if (event is FetchDevice) {
    } else if (event is UpdateFilteredDevicesEvent) {
      yield TableLoaded(event.filteredData);
    }
  }

  void _onUpdateFilteredDevices(
      UpdateFilteredDevicesEvent event, Emitter<VisitorPasswordState> emit) {
    emit(TableLoaded(event.filteredData));
  }

  addDeviceToList(context) {
    selectedDevices = selectedDeviceIds;
    Navigator.of(context).pop(selectedDevices);
  }

  Future<void> selectTimeOfLinePassword(
      SelectTimeEvent event, Emitter<VisitorPasswordState> emit) async {
    emit(ChangeTimeState());
    final DateTime? picked = await showDatePicker(
      context: event.context,
      initialDate: DateTime.now(),
      firstDate: DateTime.now(),
      lastDate: DateTime.now().add(const Duration(days: 2095)),
    );
    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!) {
            accessPeriodValidate =
                "Effective Time cannot be later than Expiration Time.";
          } else {
            accessPeriodValidate = '';
            effectiveTime = selectedDateTime.toString().split('.').first;
            effectiveTimeTimeStamp = selectedTimestamp;
          }
        } else {
          if (effectiveTimeTimeStamp != null &&
              selectedTimestamp < effectiveTimeTimeStamp!) {
            accessPeriodValidate =
                'Expiration Time cannot be earlier than Effective Time.';
          } else {
            accessPeriodValidate = '';
            expirationTime = selectedDateTime.toString().split('.').first;
            expirationTimeTimeStamp = selectedTimestamp;
          }
        }
        emit(TimeSelectedState());
      }
    }
  }

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

  DateTime? convertStringToDateTime(String dateTimeString) {
    try {
      final DateFormat inputFormat = DateFormat('yyyy-MM-dd HH:mm:ss');
      DateTime dateTime = inputFormat.parse(dateTimeString);
      return dateTime;
    } catch (e) {
      return null;
    }
  }

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

  String? validateEmail(String? value) {
    if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value!)) {
      return '';
    }
    return null;
  }

  Future stateDialog({
    BuildContext? context,
    String? message,
    String? title,
    dynamic actions,
    Widget? widgeta,
  }) {
    return showCustomDialog(
      barrierDismissible: false,
      context: context!,
      message: message!,
      iconPath: Assets.deviceNoteIcon,
      title: title,
      widget: widgeta,
      dialogHeight: MediaQuery.of(context).size.height * 0.3,
      actions: actions ??
          <Widget>[
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(true);
              },
              child: const Text('OK'),
            ),
          ],
    );
  }
}
