import 'dart:async';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:nut_player/nut_player.dart';
import 'package:nut_player_example/src/common/models/option_data.dart';
import 'package:nut_player_example/src/common/repository/settings_repository.dart';
import 'package:nut_player_example/src/features/player_screen/mapper/settings_mapper.dart';

part 'settings_event.dart';
part 'settings_state.dart';

class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
  final SettingsRepository _repository;
  StreamSubscription<String>? _versionSubscription;

  SettingsBloc(this._repository) : super(SettingsInitialState.createFromRepository(_repository)) {
    _onInitialize();
    on<DismissEvent>(_onDismissEvent);
    on<CrashEvent>(_onCrashEvent);
    on<SkinChangedEvent>(_onSkinChangedEvent);
    on<SpeedChangedEvent>(_onSpeedChangedEvent);
    on<VolumeChangedEvent>(_onVolumeChangedEvent);
    on<BrightnessChangedEvent>(_onBrightnessChangedEvent);
    on<AutostartChangedEvent>(_onAutostartChangedEvent);
    on<FullscreenChangedEvent>(_onFullscreenChangedEvent);
    on<PipChangedEvent>(_onPipChangedEvent);
    on<SettingsChangedEvent>(_onSettingsChangedEvent);
    on<ColorChangedEvent>(_onColorChangedEvent);
    on<QualityChangedEvent>(_onQualityChangedEvent);
    on<QualityNamingChangedEvent>(_onQualityNamingChangedEvent);
    on<SubsChangedEvent>(_onSubsChangedEvent);
    on<StartPositionChangedEvent>(_onStartPositionChangedEvent);
    on<LoopChangedEvent>(_onLoopChangedEvent);
    on<PlaylistTimeoutsChangedEvent>(_onPlaylistTimeoutsChangedEvent);
    on<TrackTimeoutsChangedEvent>(_onTrackTimeoutsChangedEvent);
    on<ChunkTimeoutsChangedEvent>(_onChunkTimeoutsChangedEvent);
    on<LogTypeChangedEvent>(_onLogTypeChangedEvent);
    on<VersionReceivedEvent>(_onVersionReceived);
  }

  _onInitialize() {
    _versionSubscription = PlayerVersionObserver().versionStream.listen((version) {
      add(VersionReceivedEvent(version));
    });
  }

  _onVersionReceived(VersionReceivedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    emit(settingsState.copy(playerVersion: event.version));
  }

  _onDismissEvent(DismissEvent event, Emitter<SettingsState> emit) {
    _versionSubscription?.cancel();
    _versionSubscription = null;
    emit(SettingsDismiss());
  }

  _onCrashEvent(CrashEvent event, Emitter<SettingsState> emit) {
    FirebaseCrashlytics.instance.crash();
  }

  _onSkinChangedEvent(SkinChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.isSkinByDefault = event.isSkinByDefault;
    emit(settingsState.copy(isSkinByDefault: event.isSkinByDefault));
  }

  _onSpeedChangedEvent(SpeedChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    final speedWithoutX = event.option.title.replaceAll(RegExp('x'), '');
    final speed = double.tryParse(speedWithoutX) ?? 1;

    _repository.speed = speed;
    emit(settingsState.copy(speed: speed));
  }

  _onFullscreenChangedEvent(FullscreenChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    final fullscreenMode = FullscreenSettings.values.firstWhere((element) => element.title == event.option.value);

    _repository.fullscreenSettings = SettingsMapper.repoFullscreenSettings(fullscreenMode);
    emit(settingsState.copy(fullscreenSettings: fullscreenMode));
  }

  _onVolumeChangedEvent(VolumeChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    final volume = double.parse(event.option.title);

    _repository.volume = volume;
    emit(settingsState.copy(volume: volume));
  }

  _onBrightnessChangedEvent(BrightnessChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    final brightness = double.parse(event.option.title);

    _repository.brightness = brightness;
    emit(settingsState.copy(brightness: brightness));
  }

  _onStartPositionChangedEvent(StartPositionChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.start = event.value;
    emit(settingsState.copy(start: event.value));
  }

  _onPlaylistTimeoutsChangedEvent(PlaylistTimeoutsChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.playlist = event.playlist;
    emit(settingsState.copy(playlist: event.playlist));
  }

  _onTrackTimeoutsChangedEvent(TrackTimeoutsChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.track = event.track;
    emit(settingsState.copy(track: event.track));
  }

  _onChunkTimeoutsChangedEvent(ChunkTimeoutsChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.chunk = event.chunk;
    emit(settingsState.copy(chunk: event.chunk));
  }

  _onAutostartChangedEvent(AutostartChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.isAutostart = event.isOn;
    emit(settingsState.copy(isAutostart: event.isOn));
  }

  _onLoopChangedEvent(LoopChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.isLoop = event.isOn;
    emit(settingsState.copy(isLoop: event.isOn));
  }

  _onPipChangedEvent(PipChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.isPipAvailable = event.isOn;
    emit(settingsState.copy(isPipAvailable: event.isOn));
  }

  _onSettingsChangedEvent(SettingsChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.isSettingsAvailable = event.isOn;
    emit(settingsState.copy(isSettingsAvailable: event.isOn));
  }

  _onSubsChangedEvent(SubsChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    _repository.isSubtitlesAvailable = event.isOn;
    emit(settingsState.copy(isSubtitlesAvailable: event.isOn));
  }

  _onLogTypeChangedEvent(LogTypeChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    switch (event.log) {
      case LogType.info:
        _repository.log = RepositoryLogType.info;
      case LogType.debug:
        _repository.log = RepositoryLogType.debug;
      case LogType.off:
        _repository.log = RepositoryLogType.off;
    }

    emit(settingsState.copy(log: event.log));
  }

  _onQualityChangedEvent(QualityChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    final quality = event.option.value;
    if (quality == null) { return; }
    final repoQuality = SettingsMapper.repoVideoQuality(quality);
    _repository.quality = repoQuality;
    final mappedQuality = SettingsMapper.quality(repoQuality);

    emit(settingsState.copy(quality: mappedQuality));
  }

  _onQualityNamingChangedEvent(QualityNamingChangedEvent event, Emitter<SettingsState> emit) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return; }

    final qualityNamingString = event.option.value;
    if (qualityNamingString == null) { return; }
    final repoQualityNaming = SettingsMapper.repoQualityNaming(qualityNamingString);
    _repository.qualityNaming = repoQualityNaming;

    emit(settingsState.copy(
        qualityNaming: SettingsMapper.qualityNaming(repoQualityNaming))
    );
  }

  _onColorChangedEvent(ColorChangedEvent event, Emitter<SettingsState> emit) {}

  int currentNumericValue(NumericOptionData setting) {
    if (setting.key == const Key('PlaybackOptionStartPositionID')) {
      return _repository.start;
    } else if (setting.key == const Key('TimeoutsOptionManifestID')) {
      return _repository.playlist;
    } else if (setting.key == const Key('TimeoutsOptionTrackID')) {
      return _repository.track;
    } else if (setting.key == const Key('TimeoutsOptionChunkID')) {
      return _repository.chunk;
    } else {
      return 0;
    }
  }

  int selectedIndex(OptionDataContainer setting) {
    if (setting.key == const Key('PlaybackOptionSpeedID')) {
      return _selectedIndexForSpeed(setting.options);
    } else if (setting.key == const Key('PlaybackOptionVolumeID')) {
      return _selectedIndexForVolume(setting.options);
    } else if (setting.key == const Key('PlaybackOptionBrightnessID')) {
      return _selectedIndexForBrightness(setting.options);
    } else if (setting.key == const Key('FullscreenID')) {
      return _selectedIndexForFullscreen(setting.options);
    } else if (setting.key == const Key('PlaybackOptionQualityID')) {
      return _selectedIndexForQuality(setting.options);
    } else if (setting.key == const Key('PlaybackOptionQualityNamingID')) {
      return _selectedIndexForQualityNaming(setting.options);
    }

    else {
      return 0;
    }
  }

  bool isSelected(BoolOptionData setting) {
    if (setting.key == const Key('PlaybackOptionAutostartID')) {
      return _repository.isAutostart;
    } else if (setting.key == const Key('ExtraOptionLoopID')) {
      return _repository.isLoop;
    } else if (setting.key == const Key('SkinStandardPipID')) {
      return _repository.isPipAvailable;
    } else if (setting.key == const Key('SkinStandardSettingsID')) {
      return _repository.isSettingsAvailable;
    } else if (setting.key == const Key('SkinOptionDefaultID')) {
      return _repository.isSkinByDefault;
    } else if (setting.key == const Key('SkinOptionByUserID')) {
      return !_repository.isSkinByDefault;
    } else if (setting.key == const Key('PlaybackOptionSubtitlesID')) {
      return _repository.isSubtitlesAvailable;
    } else {
      return false;
    }
  }

  int _selectedIndexForSpeed(List<OptionData> settings) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return 3; }

    return settings.indexWhere((element) => element.value == settingsState.speed.toString());
  }

  int _selectedIndexForVolume(List<OptionData> settings) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return 5; }

    return settings.indexWhere((element) => element.value == settingsState.volume.toString());
  }

  int _selectedIndexForBrightness(List<OptionData> settings) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return 5; }

    return settings.indexWhere((element) {
      double rounded = (settingsState.brightness*10).roundToDouble();
      double bright = rounded/10.0;
      return bright.toString() == element.value;
    });
  }

  int _selectedIndexForFullscreen(List<OptionData> settings) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return 0; }

    return settings.indexWhere((element) => element.value == settingsState.fullscreenSettings.title);
  }

  int _selectedIndexForQuality(List<OptionData> settings) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return 0; }

    return settings.indexWhere((element) => element.value == settingsState.quality.identifier);
  }

  int _selectedIndexForQualityNaming(List<OptionData> settings) {
    final settingsState = state;
    if (settingsState is! SettingsInitialState) { return 0; }

    return settings.indexWhere((element) => element.value == settingsState.qualityNaming.name);
  }
}
