import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:injectable/injectable.dart';
import 'package:krow/core/application/clients/api/api_client.dart';
import 'package:krow/features/profile/schedule/data/gql_schemas.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';

@injectable
class StaffScheduleApiProvider {
  final ApiClient _apiClient;

  StaffScheduleApiProvider(this._apiClient);

  Map<String, DayScheduleModel> _processScheduleDataList(
    List<dynamic> scheduleData,
  ) {
    final schedules = [
      for (final scheduleItem in scheduleData)
        ScheduleSlotModel.fromJson(
          scheduleItem as Map<String, dynamic>,
        ),
    ];

    Map<String, DayScheduleModel> scheduleMap = {};

    for (int i = 0; i < schedules.length; i++) {
      final scheduleKey = schedules[i].getIdKey();
      var scheduleDay = scheduleMap[scheduleKey];

      if (scheduleDay == null) {
        scheduleDay = DayScheduleModel(
          date: schedules[i].startAt.copyWith(
                hour: 0,
                minute: 0,
                second: 0,
              ),
          slots: [schedules[i]],
          isWeekly: schedules[i].isWeekly,
        );
        scheduleMap[scheduleKey] = scheduleDay;
      } else {
        scheduleDay.slots.add(schedules[i]);
      }
    }

    return scheduleMap;
  }

  Future<Map<String, DayScheduleModel>> _handleResultReturn(
    QueryResult<Object?> result,
    String mutationKey,
  ) async {
    if (result.hasException) throw Exception(result.exception.toString());

    if (result.data == null || result.data!.isEmpty) return {};

    final scheduleData = result.data?[mutationKey] as List<dynamic>? ?? [];

    return _processScheduleDataList(scheduleData);
  }

  Future<Map<String, DayScheduleModel>> getStaffSchedule() async {
    final response = await _apiClient.query(schema: getStaffScheduleSchema);

    if (response.hasException) {
      throw Exception(response.exception.toString());
    }

    final scheduleData = (response.data?['me']
            as Map<String, dynamic>?)?['schedule'] as List<dynamic>? ??
        [];

    return _processScheduleDataList(scheduleData);
  }

  Stream<Map<String, DayScheduleModel>> getStaffScheduleWithCache() async* {
    await for (var response in _apiClient.queryWithCache(
      schema: getStaffScheduleSchema,
    )) {
      if (response == null || response.data == null) continue;

      if (response.data == null || response.data!.isEmpty) {
        if (response.source?.name == 'cache') continue;

        if (response.hasException) {
          throw Exception(response.exception.toString());
        }
      }

      final scheduleData = (response.data?['me']
              as Map<String, dynamic>?)?['schedule'] as List<dynamic>? ??
          [];

      yield _processScheduleDataList(scheduleData);
    }
  }

  Future<Map<String, DayScheduleModel>> createStaffSchedule(
    List<ScheduleSlotModel> schedules,
  ) async {
    final result = await _apiClient.mutate(
      schema: createStaffScheduleMutationSchema,
      body: {
        'input': [
          for (final schedule in schedules) schedule.toJson(),
        ],
      },
    );

    return _handleResultReturn(result, 'create_staff_schedule');
  }

  Future<Map<String, DayScheduleModel>> updateStaffSchedule(
    List<ScheduleSlotModel> schedules,
  ) async {
    // var result = await _apiClient.mutate(
    //   schema: updateStaffScheduleMutationSchema,
    //   body: {
    //     'input': [
    //       for (final schedule in schedules) schedule.toJson(),
    //     ],
    //   },
    // );

    await Future.wait(
      [
        for (final schedule in schedules)
          _apiClient.mutate(
            schema: updateStaffScheduleMutationSchema,
            body: {
              'id': schedule.id,
              'input': schedule.toJson(),
            },
          ),
      ],
    );

    final result = await _apiClient.mutate(
      schema: updateStaffScheduleMutationSchema,
      body: {
        'id': schedules.last.id,
        'input': schedules.last.toJson(),
      },
    );

    return _handleResultReturn(result, 'update_staff_schedule');
  }

  Future<ScheduleSlotModel?> deleteStaffSchedule(
    List<ScheduleSlotModel> schedules,
  ) async {
    final result = await _apiClient.mutate(
      schema: deleteStaffScheduleMutationSchema,
      body: {
        'ids': [
          for (final schedule in schedules) schedule.id,
        ],
      },
    );

    if (result.hasException) throw Exception(result.exception.toString());

    if (result.data == null || result.data!.isEmpty) return null;

    //TODO: For now backend returns only one value so there is no
    // point in returning it
    return null;
  }

  Future<ScheduleSlotModel?> deleteStaffScheduleById(List<String> ids) async {
    final result = await _apiClient.mutate(
      schema: deleteStaffScheduleMutationSchema,
      body: {'ids': ids},
    );

    if (result.hasException) throw Exception(result.exception.toString());

    return null;
  }
}
