import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/repositories/home_repository_interface.dart';

/// Implementation of [HomeRepositoryInterface] that delegates to [HomeRepositoryMock].
///
/// This implementation resides in the data layer and acts as a bridge between the
/// domain layer and the data source (in this case, a mock from data_connect).
class HomeRepositoryImpl implements HomeRepositoryInterface {
  final HomeRepositoryMock _mock;
  final ExampleConnector _dataConnect;

  /// Creates a [HomeRepositoryImpl].
  ///
  /// Requires a [HomeRepositoryMock] to perform data operations.
  HomeRepositoryImpl(this._mock, this._dataConnect);

  @override
  Future<HomeDashboardData> getDashboardData() async {
    final String? businessId = ClientSessionStore.instance.session?.business?.id;
    if (businessId == null || businessId.isEmpty) {
      return const HomeDashboardData(
        weeklySpending: 0,
        next7DaysSpending: 0,
        weeklyShifts: 0,
        next7DaysScheduled: 0,
        totalNeeded: 0,
        totalFilled: 0,
      );
    }

    final DateTime now = DateTime.now();
    final int daysFromMonday = now.weekday - DateTime.monday;
    final DateTime monday =
        DateTime(now.year, now.month, now.day).subtract(Duration(days: daysFromMonday));
    final DateTime weekRangeStart = DateTime(monday.year, monday.month, monday.day);
    final DateTime weekRangeEnd =
        DateTime(monday.year, monday.month, monday.day + 13, 23, 59, 59, 999);
    final fdc.QueryResult<
        GetCompletedShiftsByBusinessIdData,
        GetCompletedShiftsByBusinessIdVariables> completedResult =
        await _dataConnect
            .getCompletedShiftsByBusinessId(
              businessId: businessId,
              dateFrom: _toTimestamp(weekRangeStart),
              dateTo: _toTimestamp(weekRangeEnd),
            )
            .execute();
    print(
      'Home spending: businessId=$businessId dateFrom=${weekRangeStart.toIso8601String()} '
      'dateTo=${weekRangeEnd.toIso8601String()} shifts=${completedResult.data.shifts.length}',
    );

    double weeklySpending = 0.0;
    double next7DaysSpending = 0.0;
    int weeklyShifts = 0;
    int next7DaysScheduled = 0;
    for (final GetCompletedShiftsByBusinessIdShifts shift
        in completedResult.data.shifts) {
      final DateTime? shiftDate = shift.date?.toDateTime();
      if (shiftDate == null) {
        continue;
      }
      final int offset = shiftDate.difference(weekRangeStart).inDays;
      if (offset < 0 || offset > 13) {
        continue;
      }
      final double cost = shift.cost ?? 0.0;
      if (offset <= 6) {
        weeklySpending += cost;
        weeklyShifts += 1;
      } else {
        next7DaysSpending += cost;
        next7DaysScheduled += 1;
      }
    }

    final DateTime start = DateTime(now.year, now.month, now.day);
    final DateTime end = DateTime(now.year, now.month, now.day, 23, 59, 59, 999);

    final fdc.QueryResult<
        ListShiftRolesByBusinessAndDateRangeData,
        ListShiftRolesByBusinessAndDateRangeVariables> result =
        await _dataConnect
            .listShiftRolesByBusinessAndDateRange(
              businessId: businessId,
              start: _toTimestamp(start),
              end: _toTimestamp(end),
            )
            .execute();
    print(
      'Home coverage: businessId=$businessId '
      'startLocal=${start.toIso8601String()} '
      'endLocal=${end.toIso8601String()} '
      'startUtc=${_toTimestamp(start).toJson()} '
      'endUtc=${_toTimestamp(end).toJson()} '
      'shiftRoles=${result.data.shiftRoles.length}',
    );

    int totalNeeded = 0;
    int totalFilled = 0;
    for (final ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole
        in result.data.shiftRoles) {
      totalNeeded += shiftRole.count;
      totalFilled += shiftRole.assigned ?? 0;
    }

    return HomeDashboardData(
      weeklySpending: weeklySpending,
      next7DaysSpending: next7DaysSpending,
      weeklyShifts: weeklyShifts,
      next7DaysScheduled: next7DaysScheduled,
      totalNeeded: totalNeeded,
      totalFilled: totalFilled,
    );
  }

  @override
  UserSessionData getUserSessionData() {
    final (String businessName, String? photoUrl) = _mock.getUserSession();
    return UserSessionData(
      businessName: businessName,
      photoUrl: photoUrl,
    );
  }

  @override
  Future<List<ReorderItem>> getRecentReorders() async {
    final String? businessId = ClientSessionStore.instance.session?.business?.id;
    if (businessId == null || businessId.isEmpty) {
      return const <ReorderItem>[];
    }

    final DateTime now = DateTime.now();
    final DateTime start = now.subtract(const Duration(days: 30));
    final fdc.Timestamp startTimestamp = _toTimestamp(start);
    final fdc.Timestamp endTimestamp = _toTimestamp(now);

    final fdc.QueryResult<
        ListShiftRolesByBusinessDateRangeCompletedOrdersData,
        ListShiftRolesByBusinessDateRangeCompletedOrdersVariables> result =
        await _dataConnect.listShiftRolesByBusinessDateRangeCompletedOrders(
          businessId: businessId,
          start: startTimestamp,
          end: endTimestamp,
        ).execute();

    print(
      'Home reorder: completed shiftRoles=${result.data.shiftRoles.length}',
    );

    return result.data.shiftRoles.map((
      ListShiftRolesByBusinessDateRangeCompletedOrdersShiftRoles shiftRole,
    ) {
      print(
        'Home reorder item: orderId=${shiftRole.shift.order.id} '
        'shiftId=${shiftRole.shiftId} roleId=${shiftRole.roleId} '
        'orderType=${shiftRole.shift.order.orderType.stringValue} '
        'hours=${shiftRole.hours} count=${shiftRole.count}',
      );
      final String location =
          shiftRole.shift.location ??
          shiftRole.shift.locationAddress ??
          '';
      final String type = shiftRole.shift.order.orderType.stringValue;
      return ReorderItem(
        orderId: shiftRole.shift.order.id,
        title: '${shiftRole.role.name} - ${shiftRole.shift.title}',
        location: location,
        hourlyRate: shiftRole.role.costPerHour,
        hours: shiftRole.hours ?? 0,
        workers: shiftRole.count,
        type: type,
      );
    }).toList();
  }

  fdc.Timestamp _toTimestamp(DateTime date) {
    final DateTime utc = date.toUtc();
    final int millis = utc.millisecondsSinceEpoch;
    final int seconds = millis ~/ 1000;
    final int nanos = (millis % 1000) * 1000000;
    return fdc.Timestamp(nanos, seconds);
  }
}
