import 'package:injectable/injectable.dart';
import 'package:krow/features/profile/schedule/data/models/day_schedule_model.dart';
import 'package:krow/features/profile/schedule/data/models/schedule_slot_model.dart';
import 'package:krow/features/profile/schedule/data/staff_schedule_api_provider.dart';
import 'package:krow/features/profile/schedule/domain/entities/day_shedule.dart';
import 'package:krow/features/profile/schedule/domain/entities/schedule_slot.dart';
import 'package:krow/features/profile/schedule/domain/staff_schedule_repository.dart';

@Injectable(as: StaffScheduleRepository)
class StaffScheduleRepositoryImpl extends StaffScheduleRepository {
  StaffScheduleRepositoryImpl({
    required StaffScheduleApiProvider apiProvider,
  }) : _apiProvider = apiProvider;

  final StaffScheduleApiProvider _apiProvider;

  Map<String, DaySchedule> _processSchedulesMap(
    Map<String, DayScheduleModel> daySchedulesData,
  ) {
    return daySchedulesData.map(
      (key, dayEntry) {
        return MapEntry(
          key,
          DaySchedule(
            date: dayEntry.date,
            isWeekly: dayEntry.isWeekly,
            slots: [
              for (final slot in dayEntry.slots)
                ScheduleSlot(
                  id: slot.startAt.microsecondsSinceEpoch.toString(),
                  startTime: slot.startAt,
                  endTime: slot.endAt,
                  remoteId: slot.id,
                ),
            ],
          ),
        );
      },
    );
  }

  List<ScheduleSlotModel> _expandDailySchedulesToSlots(
    List<DaySchedule> schedules,
  ) {
    return [
      for (final daySchedule in schedules) ...[
        for (final slot in daySchedule.slots)
          ScheduleSlotModel(
            isWeekly: daySchedule.isWeekly,
            startAt: slot.startTime,
            endAt: slot.endTime,
            id: slot.remoteId,
          ),
      ],
    ];
  }

  @override
  Stream<Map<String, DaySchedule>> getStaffSchedule() async* {
    await for (final daySchedulesData
        in _apiProvider.getStaffScheduleWithCache()) {
      yield _processSchedulesMap(daySchedulesData);
    }
  }

  @override
  Future<Map<String, DaySchedule>> createStaffSchedule({
    required List<DaySchedule> schedules,
  }) async {
    return _processSchedulesMap(
      await _apiProvider.createStaffSchedule(
        _expandDailySchedulesToSlots(schedules),
      ),
    );
  }

  @override
  Future<Map<String, DaySchedule>> updateStaffSchedule({
    required List<DaySchedule> schedules,
  }) async {
    if (schedules.length == 1) {
      if (schedules.first.deletedSlots != null) {
        await _apiProvider.deleteStaffScheduleById(
          [
            for (final deletedSlot in schedules.first.deletedSlots!)
              deletedSlot.remoteId ?? '',
          ],
        );
      }

      // In case there were only deletion of slots for the day, and no edits
      if (schedules.first.slots.isEmpty) {
        return _processSchedulesMap(
          await _apiProvider.getStaffSchedule(),
        );
      }
    }

    return _processSchedulesMap(
      await _apiProvider.updateStaffSchedule(
        _expandDailySchedulesToSlots(schedules),
      ),
    );
  }

  @override
  Future<DaySchedule?> deleteStaffSchedule({
    required List<DaySchedule> schedules,
  }) async {
    await _apiProvider.deleteStaffSchedule(
      _expandDailySchedulesToSlots(schedules),
    );

    return null;
  }
}
