import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:intl/intl.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart' as domain;
import '../../domain/repositories/i_view_orders_repository.dart';

/// Implementation of [IViewOrdersRepository] using Data Connect.
class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
  final firebase.FirebaseAuth _firebaseAuth;
  final dc.ExampleConnector _dataConnect;

  ViewOrdersRepositoryImpl({
    required firebase.FirebaseAuth firebaseAuth,
    required dc.ExampleConnector dataConnect,
  }) : _firebaseAuth = firebaseAuth,
       _dataConnect = dataConnect;

  @override
  Future<List<domain.OrderItem>> getOrdersForRange({
    required DateTime start,
    required DateTime end,
  }) async {
    final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
    if (businessId == null || businessId.isEmpty) {
      await _firebaseAuth.signOut();
      throw Exception('Business is missing. Please sign in again.');
    }

    final fdc.Timestamp startTimestamp = _toTimestamp(_startOfDay(start));
    final fdc.Timestamp endTimestamp = _toTimestamp(_endOfDay(end));
    final fdc.QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData, dc.ListShiftRolesByBusinessAndDateRangeVariables> result = await _dataConnect
        .listShiftRolesByBusinessAndDateRange(
          businessId: businessId,
          start: startTimestamp,
          end: endTimestamp,
        )
        .execute();

    final String businessName =
        dc.ClientSessionStore.instance.session?.business?.businessName ??
        'Your Company';

    return result.data.shiftRoles.map((dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole) {
      final DateTime? shiftDate = shiftRole.shift.date?.toDateTime();
      final String dateStr = shiftDate == null
          ? ''
          : DateFormat('yyyy-MM-dd').format(shiftDate);
      final String startTime = _formatTime(shiftRole.startTime);
      final String endTime = _formatTime(shiftRole.endTime);
      final int filled = shiftRole.assigned ?? 0;
      final int workersNeeded = shiftRole.count;
      final double hours = shiftRole.hours ?? 0;
      final double totalValue = shiftRole.totalValue ?? 0;
      final double hourlyRate = _hourlyRate(shiftRole.totalValue, shiftRole.hours);
      // final String status = filled >= workersNeeded ? 'filled' : 'open';
      final String status = shiftRole.shift.status?.stringValue ?? 'OPEN';

      print(
        'ViewOrders item: date=$dateStr status=$status shiftId=${shiftRole.shiftId} '
        'roleId=${shiftRole.roleId} start=${shiftRole.startTime?.toJson()} '
        'end=${shiftRole.endTime?.toJson()} hours=$hours totalValue=$totalValue',
      );

      return domain.OrderItem(
        id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId),
        orderId: shiftRole.shift.order.id,
        title: '${shiftRole.role.name} - ${shiftRole.shift.title}',
        clientName: businessName,
        status: status,
        date: dateStr,
        startTime: startTime,
        endTime: endTime,
        location: shiftRole.shift.location ?? '',
        locationAddress: shiftRole.shift.locationAddress ?? '',
        filled: filled,
        workersNeeded: workersNeeded,
        hourlyRate: hourlyRate,
        hours: hours,
        totalValue: totalValue,
        confirmedApps: const <Map<String, dynamic>>[],
      );
    }).toList();
  }

  @override
  Future<Map<String, List<Map<String, dynamic>>>> getAcceptedApplicationsForDay(
    DateTime day,
  ) async {
    final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
    if (businessId == null || businessId.isEmpty) {
      await _firebaseAuth.signOut();
      throw Exception('Business is missing. Please sign in again.');
    }

    final fdc.Timestamp dayStart = _toTimestamp(_startOfDay(day));
    final fdc.Timestamp dayEnd = _toTimestamp(_endOfDay(day));
    final fdc.QueryResult<dc.ListAcceptedApplicationsByBusinessForDayData, dc.ListAcceptedApplicationsByBusinessForDayVariables> result = await _dataConnect
        .listAcceptedApplicationsByBusinessForDay(
          businessId: businessId,
          dayStart: dayStart,
          dayEnd: dayEnd,
        )
        .execute();

    final Map<String, List<Map<String, dynamic>>> grouped = <String, List<Map<String, dynamic>>>{};
    for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application in result.data.applications) {
      print(
        'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} '
        'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}',
      );
      final String key = _shiftRoleKey(application.shiftId, application.roleId);
      grouped.putIfAbsent(key, () => <Map<String, dynamic>>[]);
      grouped[key]!.add(<String, dynamic>{
        'id': application.id,
        'worker_id': application.staff.id,
        'worker_name': application.staff.fullName,
        'status': 'confirmed',
        'photo_url': application.staff.photoUrl,
      });
    }
    return grouped;
  }

  String _shiftRoleKey(String shiftId, String roleId) {
    return '$shiftId:$roleId';
  }

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

  DateTime _startOfDay(DateTime dateTime) {
    return DateTime(dateTime.year, dateTime.month, dateTime.day);
  }

  DateTime _endOfDay(DateTime dateTime) {
    return DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59);
  }

  String _formatTime(fdc.Timestamp? timestamp) {
    if (timestamp == null) {
      return '';
    }
    final DateTime dateTime = timestamp.toDateTime();
    return DateFormat('HH:mm').format(dateTime);
  }

  double _hourlyRate(double? totalValue, double? hours) {
    if (totalValue == null || hours == null || hours == 0) {
      return 0;
    }
    return totalValue / hours;
  }
}
