import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
import 'package:syncrow_web/services/control_device_service.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
part 'schedule_event.dart';
part 'schedule_state.dart';

class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
  final String deviceId;

  ScheduleBloc({
    required this.deviceId,
  }) : super(ScheduleInitial()) {
    on<ScheduleInitializeAddEvent>(_initializeAddSchedule);
    on<ScheduleUpdateSelectedTimeEvent>(_updateSelectedTime);
    on<ScheduleUpdateSelectedDayEvent>(_updateSelectedDay);
    on<ScheduleUpdateFunctionOnEvent>(_updateFunctionOn);
    on<ScheduleGetEvent>(_getSchedule);
    on<ScheduleAddEvent>(_onAddSchedule);
    on<ScheduleEditEvent>(_onEditSchedule);
    on<ScheduleUpdateEntryEvent>(_onUpdateSchedule);
    on<UpdateScheduleModeEvent>(_onUpdateScheduleMode);
    on<UpdateCountdownTimeEvent>(_onUpdateCountdownTime);
    on<UpdateInchingTimeEvent>(_onUpdateInchingTime);
    on<StartScheduleEvent>(_onStartScheduleEvent);
    on<StopScheduleEvent>(_onStopScheduleEvent);
    on<ScheduleDecrementCountdownEvent>(_onDecrementCountdown);
    on<ScheduleFetchStatusEvent>(_fetchStatus);
    on<ScheduleDeleteEvent>(_onDeleteSchedule);
  }
  Timer? _countdownTimer;
  Duration countdownRemaining = Duration.zero;

  Future<void> _onStopScheduleEvent(
    StopScheduleEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;

      final success = await RemoteControlDeviceService().controlDevice(
        deviceUuid: deviceId,
        status: Status(
          code: event.countdownCode,
          value: 0,
        ),
      );
      if (success) {
        _countdownTimer?.cancel();
        if (event.mode == ScheduleModes.countdown) {
          emit(currentState.copyWith(
            countdownHours: 0,
            countdownMinutes: 0,
            isCountdownActive: false,
            countdownRemaining: Duration.zero,
          ));
        } else if (event.mode == ScheduleModes.inching) {
          emit(currentState.copyWith(
            inchingHours: 0,
            inchingMinutes: 0,
            isInchingActive: false,
            countdownRemaining: Duration.zero,
          ));
        }
      } else {
        emit(const ScheduleError('Failed to stop schedule'));
      }
    }
  }

  void _onUpdateScheduleMode(
    UpdateScheduleModeEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;

      emit(currentState.copyWith(
        countdownSeconds: currentState.countdownSeconds,
        selectedTime: currentState.selectedTime,
        deviceId: deviceId,
        scheduleMode: event.scheduleMode,
        countdownHours: currentState.countdownHours,
        countdownMinutes: currentState.countdownMinutes,
        inchingHours: currentState.inchingHours,
        inchingMinutes: currentState.inchingMinutes,
        isInchingActive: false,
        isCountdownActive: currentState.countdownRemaining > Duration.zero,
      ));
    }
  }

  void _onUpdateCountdownTime(
    UpdateCountdownTimeEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;
      emit(currentState.copyWith(
        countdownSeconds: event.seconds,
        countdownHours: event.hours,
        countdownMinutes: event.minutes,
        inchingHours: 0,
        inchingMinutes: 0,
        countdownRemaining: Duration.zero,
      ));
    }
  }

  void _onUpdateInchingTime(
    UpdateInchingTimeEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;
      emit(currentState.copyWith(
        inchingHours: event.hours,
        inchingMinutes: event.minutes,
        countdownRemaining: Duration.zero,
        inchingSeconds: 0, // Add this
      ));
    }
  }

  void _initializeAddSchedule(
    ScheduleInitializeAddEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;
      emit(currentState.copyWith(
        selectedTime: event.selectedTime,
        selectedDays: event.selectedDays ?? List.filled(7, false),
        functionOn: event.functionOn ?? false,
        isEditing: event.isEditing,
        scheduleMode: event.scheduleMode,
        countdownRemaining: Duration.zero,
      ));
    } else {
      emit(ScheduleLoaded(
        schedules: const [],
        selectedTime: event.selectedTime,
        selectedDays: event.selectedDays ?? List.filled(7, false),
        functionOn: event.functionOn ?? false,
        isEditing: event.isEditing,
        deviceId: deviceId,
        scheduleMode: event.scheduleMode,
        countdownHours: 0,
        countdownMinutes: 0,
        inchingHours: 0,
        inchingMinutes: 0,
        isCountdownActive: false,
        isInchingActive: false,
      ));
    }
  }

  void _updateSelectedTime(
    ScheduleUpdateSelectedTimeEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;
      emit(currentState.copyWith(
        selectedTime: event.selectedTime,
        countdownHours: 0,
        countdownMinutes: 0,
        inchingHours: 0,
        inchingMinutes: 0,
        countdownRemaining: Duration.zero,
      ));
    }
  }

  void _updateSelectedDay(
    ScheduleUpdateSelectedDayEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;
      final updatedDays = List<bool>.from(currentState.selectedDays);
      updatedDays[event.index] = event.value;
      emit(currentState.copyWith(
        selectedDays: updatedDays,
        countdownHours: 0,
        countdownMinutes: 0,
        inchingHours: 0,
        inchingMinutes: 0,
        countdownRemaining: Duration.zero,
      ));
    }
  }

  void _updateFunctionOn(
    ScheduleUpdateFunctionOnEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;
      emit(currentState.copyWith(
        functionOn: event.isOn,
        countdownHours: 0,
        countdownMinutes: 0,
        inchingHours: 0,
        inchingMinutes: 0,
        countdownRemaining: Duration.zero,
      ));
    }
  }

  Future<void> _getSchedule(
    ScheduleGetEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    try {
      emit(ScheduleLoading());
      final schedules = await DevicesManagementApi().getDeviceSchedules(
        deviceId,
        event.category,
      );
      if (state is ScheduleLoaded) {
        final currentState = state as ScheduleLoaded;
        emit(currentState.copyWith(
          schedules: schedules,
          selectedTime: null,
          selectedDays: List.filled(7, false),
          functionOn: false,
          isEditing: false,
        ));
      } else {
        emit(ScheduleLoaded(
          schedules: schedules,
          selectedTime: null,
          selectedDays: List.filled(7, false),
          functionOn: false,
          isEditing: false,
          deviceId: deviceId,
          scheduleMode: ScheduleModes.schedule,
          countdownHours: 0,
          countdownMinutes: 0,
          inchingHours: 0,
          inchingMinutes: 0,
          isCountdownActive: false,
          isInchingActive: false,
        ));
      }
    } catch (e) {
      emit(ScheduleError('Failed to load schedules: $e'));
    }
  }

  Future<void> _onAddSchedule(
    ScheduleAddEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    try {
      if (state is ScheduleLoaded) {
        final dateTime = DateTime.parse(event.time);
        final success = await DevicesManagementApi().postSchedule(
            category: event.category,
            deviceId: deviceId,
            time: getTimeStampWithoutSeconds(dateTime).toString(),
            code: event.code ?? event.category,
            value: event.functionOn,
            days: event.selectedDays);
        if (success) {
          add(ScheduleGetEvent(category: event.category));
        } else {
          emit(const ScheduleError('Failed to add schedule'));
        }
      }
    } catch (e) {
      emit(ScheduleError('Failed to add schedule: $e'));
    }
  }

  Future<void> _onEditSchedule(
    ScheduleEditEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    try {
      if (state is ScheduleLoaded) {
        Status status = Status(code: '', value: '');
        if (event.deviceType == 'CUR_2') {
          status = status.copyWith(
              code: 'control',
              value: event.functionOn == true ? 'open' : 'close');
        } else {
          status =
              status.copyWith(code: event.category, value: event.functionOn);
        }

        final dateTime = DateTime.parse(event.time);
        final updatedSchedule = ScheduleEntry(
          scheduleId: event.scheduleId,
          category: event.category,
          time: getTimeStampWithoutSeconds(dateTime).toString(),
          function: status,
          days: event.selectedDays,
        );
        final success = await DevicesManagementApi().editScheduleRecord(
          deviceId,
          updatedSchedule,
        );

        if (success) {
          add(ScheduleGetEvent(
            category: event.category,
          ));
        } else {
          emit(const ScheduleError('Failed to update schedule'));
        }
      }
    } catch (e) {
      emit(ScheduleError('Failed to update schedule: $e'));
    }
  }

  Future<void> _onUpdateSchedule(
    ScheduleUpdateEntryEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    try {
      if (state is ScheduleLoaded) {
        final currentState = state as ScheduleLoaded;

        final updatedSchedules = currentState.schedules.map((schedule) {
          if (schedule.scheduleId == event.scheduleId) {
            return schedule.copyWith(
              function: Status(code: event.category, value: event.functionOn),
              enable: event.enable,
            );
          }
          return schedule;
        }).toList();

        final success = await DevicesManagementApi().updateScheduleRecord(
          enable: event.enable,
          uuid: deviceId,
          scheduleId: event.scheduleId,
        );

        if (success) {
          emit(currentState.copyWith(
            schedules: updatedSchedules,
            countdownHours: 0,
            countdownMinutes: 0,
            inchingHours: 0,
            inchingMinutes: 0,
            countdownRemaining: Duration.zero,
          ));
        } else {
          emit(const ScheduleError('Failed to update schedule status'));
        }
      }
    } catch (e) {
      emit(ScheduleError('Failed to update schedule: $e'));
    }
  }

  Future<void> _onDeleteSchedule(
    ScheduleDeleteEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    try {
      if (state is ScheduleLoaded) {
        final currentState = state as ScheduleLoaded;
        final success = await DevicesManagementApi().deleteScheduleRecord(
          deviceId,
          event.scheduleId,
        );

        if (success) {
          final updatedSchedules = currentState.schedules
              .where((s) => s.scheduleId != event.scheduleId)
              .toList();
          emit(currentState.copyWith(
            schedules: updatedSchedules,
            countdownHours: 0,
            countdownMinutes: 0,
            inchingHours: 0,
            inchingMinutes: 0,
            countdownRemaining: Duration.zero,
          ));
        } else {
          emit(const ScheduleError('Failed to delete schedule'));
        }
      }
    } catch (e) {
      emit(ScheduleError('Failed to delete schedule: $e'));
    }
  }

  Duration? _currentCountdown;

  Future<void> _onStartScheduleEvent(
    StartScheduleEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    if (state is ScheduleLoaded) {
      final totalSeconds =
          Duration(hours: event.hours, minutes: event.minutes).inSeconds;
      final code = event.mode == ScheduleModes.countdown
          ? event.countDownCode
          : 'switch_inching';
      final currentState = state as ScheduleLoaded;
      final duration = Duration(seconds: totalSeconds);
      _currentCountdown = duration;
      emit(currentState.copyWith(
        countdownRemaining: duration,
        schedules: currentState.schedules.map((schedule) {
          if (schedule.function.code == code) {
            return schedule.copyWith(
              function: Status(code: code, value: totalSeconds),
            );
          }
          return schedule;
        }).toList(),
        countdownHours: event.mode == ScheduleModes.countdown ? event.hours : 0,
      ));

      final success = await RemoteControlDeviceService().controlDevice(
        deviceUuid: deviceId,
        status: Status(
          code: code,
          value: totalSeconds,
        ),
      );

      if (success) {
        if (code == event.countDownCode) {
          final countdownDuration = Duration(seconds: totalSeconds);

          emit(
            currentState.copyWith(
              countdownHours: countdownDuration.inHours,
              countdownMinutes: countdownDuration.inMinutes % 60,
              countdownRemaining: countdownDuration,
              isCountdownActive: true,
              countdownSeconds: countdownDuration.inSeconds,
            ),
          );

          if (countdownDuration.inSeconds > 0) {
            _startCountdownTimer(emit, countdownDuration, event.countDownCode);
          } else {
            _countdownTimer?.cancel();
            emit(
              currentState.copyWith(
                countdownHours: 0,
                countdownMinutes: 0,
                countdownRemaining: Duration.zero,
                isCountdownActive: false,
                countdownSeconds: 0,
              ),
            );
          }
        } else if (code == 'switch_inching') {
          final inchingDuration = Duration(seconds: totalSeconds);
          emit(
            currentState.copyWith(
              inchingHours: inchingDuration.inHours,
              inchingMinutes: inchingDuration.inMinutes % 60,
              isInchingActive: true,
              countdownRemaining: inchingDuration,
              countdownSeconds: inchingDuration.inSeconds,
            ),
          );
        }
      }
    }
  }

  void _startCountdownTimer(
      Emitter<ScheduleState> emit, Duration duration, String countdownCode) {
    _countdownTimer?.cancel();
    _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (_currentCountdown != null && _currentCountdown! > Duration.zero) {
        _currentCountdown = _currentCountdown! - const Duration(seconds: 1);
        countdownRemaining = _currentCountdown!;
        add(const ScheduleDecrementCountdownEvent());
      } else {
        timer.cancel();
        add(StopScheduleEvent(
          countdownCode: countdownCode,
          mode: _currentCountdown == null
              ? ScheduleModes.countdown
              : ScheduleModes.inching,
          deviceId: deviceId,
        ));
      }
    });
  }

  void _onDecrementCountdown(
    ScheduleDecrementCountdownEvent event,
    Emitter<ScheduleState> emit,
  ) {
    if (state is ScheduleLoaded) {
      final currentState = state as ScheduleLoaded;
      emit(currentState.copyWith(
        countdownRemaining: countdownRemaining,
      ));
    }
  }

  @override
  Future<void> close() {
    _countdownTimer?.cancel();
    return super.close();
  }

  Future<void> _fetchStatus(
    ScheduleFetchStatusEvent event,
    Emitter<ScheduleState> emit,
  ) async {
    emit(ScheduleLoading());

    try {
      final status =
          await DevicesManagementApi().getDeviceStatus(event.deviceId);
      int totalSeconds = 0;
      final countdownItem = status.status.firstWhere(
        (item) => item.code == event.countdownCode,
        orElse: () => Status(code: '', value: 0),
      );
      totalSeconds = (countdownItem.value as int?) ?? 0;
      final countdownHours = totalSeconds ~/ 3600;
      final countdownMinutes = (totalSeconds % 3600) ~/ 60;
      final countdownSeconds = totalSeconds % 60;

      final deviceStatus =
          WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
      final isCountdownActive = totalSeconds > 0;
      final isInchingActive = !isCountdownActive &&
          (deviceStatus.inchingHours > 0 || deviceStatus.inchingMinutes > 0);

      final newState = state is ScheduleLoaded
          ? (state as ScheduleLoaded).copyWith(
              scheduleMode: ScheduleModes.schedule,
              countdownHours: countdownHours,
              countdownMinutes: countdownMinutes,
              countdownSeconds: countdownSeconds,
              inchingHours: deviceStatus.inchingHours,
              inchingMinutes: deviceStatus.inchingMinutes,
              isCountdownActive: isCountdownActive,
              isInchingActive: isInchingActive,
              countdownRemaining: isCountdownActive
                  ? Duration(seconds: totalSeconds)
                  : Duration.zero,
            )
          : ScheduleLoaded(
              scheduleMode: ScheduleModes.schedule,
              schedules: const [],
              selectedTime: null,
              selectedDays: List.filled(7, false),
              functionOn: false,
              isEditing: false,
              deviceId: event.deviceId,
              countdownHours: countdownHours,
              countdownMinutes: countdownMinutes,
              countdownSeconds: countdownSeconds,
              inchingHours: deviceStatus.inchingHours,
              inchingMinutes: deviceStatus.inchingMinutes,
              isCountdownActive: isCountdownActive,
              isInchingActive: isInchingActive,
              countdownRemaining: isCountdownActive
                  ? Duration(seconds: totalSeconds)
                  : Duration.zero,
            );
      emit(newState);

      if (isCountdownActive) {
        _countdownTimer?.cancel();
        _currentCountdown = Duration(seconds: totalSeconds);
        countdownRemaining = _currentCountdown!;

        if (totalSeconds > 0) {
          _startCountdownTimer(
              emit, Duration(seconds: totalSeconds), event.countdownCode);
        } else {
          add(StopScheduleEvent(
            countdownCode: event.countdownCode,
            mode: ScheduleModes.countdown,
            deviceId: event.deviceId,
          ));
        }
      } else {
        _countdownTimer?.cancel();
      }
    } catch (e) {
      emit(ScheduleError('Failed to fetch device status: $e'));
    }
  }

  String extractTime(String isoDateTime) {
    return isoDateTime.split('T')[1].split('.')[0];
  }

  int? getTimeStampWithoutSeconds(DateTime? dateTime) {
    if (dateTime == null) return null;
    DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
        dateTime.day, dateTime.hour, dateTime.minute);
    return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
  }
}
