import 'package:flutter_bloc/flutter_bloc.dart';

import '../../../data/services/coordination_service.dart';
import '../../../domain/entities/sequence.dart';
import 'sequence_event.dart';
import 'sequence_state.dart';

class SequenceBloc extends Bloc<SequenceEvent, SequenceState> {
  final CoordinationService _coordinationService;

  SequenceBloc({required CoordinationService coordinationService})
      : _coordinationService = coordinationService,
        super(const SequenceState()) {
    on<LoadSequences>(_onLoadSequences);
    on<StartSequence>(_onStartSequence);
    on<StopSequence>(_onStopSequence);
    on<SelectCategory>(_onSelectCategory);
  }

  Future<void> _onLoadSequences(
      LoadSequences event, Emitter<SequenceState> emit) async {
    emit(state.copyWith(isLoading: true, clearError: true));

    try {
      final sequencesJson = await _coordinationService.getSequences();
      final categoriesJson = await _coordinationService.getSequenceCategories();
      final runningJson = await _coordinationService.getRunningSequences();

      final sequences = sequencesJson
          .map((j) => SequenceDefinition.fromJson(j))
          .toList();
      final categories =
          categoriesJson.map((j) => SequenceCategory.fromJson(j)).toList();

      final running = <int, RunningSequence>{};
      for (final j in runningJson) {
        final rs = RunningSequence.fromJson(j);
        running[rs.viewerId] = rs;
      }

      emit(state.copyWith(
        sequences: sequences,
        categories: categories,
        running: running,
        isLoading: false,
      ));
    } catch (e) {
      emit(state.copyWith(isLoading: false, error: e.toString()));
    }
  }

  Future<void> _onStartSequence(
      StartSequence event, Emitter<SequenceState> emit) async {
    try {
      final result = await _coordinationService.startSequence(
          event.viewerId, event.sequenceId);

      if (result != null) {
        final rs = RunningSequence.fromJson(result);
        final running = Map<int, RunningSequence>.from(state.running);
        running[rs.viewerId] = rs;
        emit(state.copyWith(running: running));
      }
    } catch (e) {
      emit(state.copyWith(error: e.toString()));
    }
  }

  Future<void> _onStopSequence(
      StopSequence event, Emitter<SequenceState> emit) async {
    try {
      await _coordinationService.stopSequence(event.viewerId);

      final running = Map<int, RunningSequence>.from(state.running);
      running.remove(event.viewerId);
      emit(state.copyWith(running: running));
    } catch (e) {
      emit(state.copyWith(error: e.toString()));
    }
  }

  void _onSelectCategory(
      SelectCategory event, Emitter<SequenceState> emit) {
    if (event.categoryId == null) {
      emit(state.copyWith(clearCategory: true));
    } else {
      emit(state.copyWith(selectedCategoryId: event.categoryId));
    }
  }
}
