import 'dart:async';

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/data/enums/staff_skill_enums.dart';
import 'package:krow/core/data/models/skill.dart';
import 'package:krow/core/data/models/staff_role.dart';
import 'package:krow/features/profile/role/domain/bloc/role_event.dart';
import 'package:krow/features/profile/role/domain/bloc/role_state.dart';
import 'package:krow/features/profile/role/domain/staff_role_repository.dart';

class RoleBloc extends Bloc<RoleEvent, RoleState> {
  List<Skill>? skills;
  List<StaffRole> staffRoles = [];

  RoleBloc() : super(const RoleState(staffRoles: [], isLoading: true)) {
    on<RoleFetchEvent>(_onFetch);
    on<RoleSelectEvent>(_onSelect);
    on<RoleSpawnEvent>(_onSpawn);
    on<RoleDeleteEvent>(_onDelete);
    on<RoleUpdateEvent>(_onUpdate);
    on<RoleSaveChangesEvent>(_onSaveChanges);
  }

  FutureOr<void> _onFetch(event, emit) async {
    staffRoles = await getIt<StaffRoleRepository>().getStaffRole();
    skills = await getIt<StaffRoleRepository>().getSkills();
    var availableSkills = skills!
        .where((e) => !staffRoles.any((r) => r.skill?.id == e.id))
        .toList();
    emit(RoleState(
        staffRoles: staffRoles, skills: availableSkills, isLoading: false));

    if (staffRoles.isEmpty) {
      var staffRole = StaffRole(
          id: DateTime.now().toIso8601String(),
          level: StaffSkillLevel.beginner,
          experience: 1);
      emit(state.copyWith(editedRole: staffRole, staffRoles: [staffRole]));
    }
  }

  FutureOr<void> _onSelect(RoleSelectEvent event, emit) async {
    emit(state.copyWith(editedRole: event.editedRole));
  }

  FutureOr<void> _onSpawn(event, emit) async {
    var newRole = StaffRole(
        id: DateTime.now().toIso8601String(),
        level: StaffSkillLevel.beginner,
        experience: 1);
    emit(state.copyWith(
        editedRole: newRole, staffRoles: [...state.staffRoles, newRole]));
  }

  FutureOr<void> _onDelete(RoleDeleteEvent event, emit) async {
    emit(state.copyWith(isLoading: true));
    if (event.role.status != null) {
      await getIt<StaffRoleRepository>().deleteStaffRole(event.role);
    }
    var roles = state.staffRoles.where((e) => e != event.role).toList();
    var availableSkills =
        skills!.where((e) => !roles.any((r) => r.skill?.id == e.id)).toList();

    emit(state.copyWith(
        editedRole:
            event.role.id == state.editedRole?.id ? null : state.editedRole,
        staffRoles: roles,
        skills: availableSkills,
        isLoading: false));
  }

  FutureOr<void> _onUpdate(RoleUpdateEvent event, emit) async {
    var roles = state.staffRoles
        .map((e) => e.id == event.role.id ? event.role : e)
        .toList();

    var availableSkills =
        skills!.where((e) => !roles.any((r) => r.skill?.id == e.id)).toList();
    emit(state.copyWith(
        staffRoles: roles, editedRole: event.role, skills: availableSkills));
  }

  Future<void> _onSaveChanges(RoleSaveChangesEvent event, emit) async {
    emit(state.copyWith(isLoading: true));
    await getIt<StaffRoleRepository>().saveStaffRole(event.role);

    List<StaffRole> roles = state.staffRoles
        .map((e) => e.id == event.role.id
            ? event.role.copyWith(status: StaffSkillStatus.pending)
            : e)
        .toList();
    emit(state.copyWith(
      staffRoles: roles,
      isLoading: false,
      editedRole: StaffRole.empty(),
    ));
  }
}
