import 'package:intl/intl.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/arguments/orders_day_arguments.dart';
import '../../domain/arguments/orders_range_arguments.dart';
import '../../domain/usecases/get_accepted_applications_for_day_use_case.dart';
import '../../domain/usecases/get_orders_use_case.dart';
import 'view_orders_state.dart';

/// Cubit for managing the state of the View Orders feature.
///
/// This Cubit handles loading orders, date selection, and tab filtering.
class ViewOrdersCubit extends Cubit<ViewOrdersState>
    with BlocErrorHandler<ViewOrdersState> {
  ViewOrdersCubit({
    required GetOrdersUseCase getOrdersUseCase,
    required GetAcceptedApplicationsForDayUseCase getAcceptedAppsUseCase,
  }) : _getOrdersUseCase = getOrdersUseCase,
       _getAcceptedAppsUseCase = getAcceptedAppsUseCase,
       super(ViewOrdersState(selectedDate: DateTime.now())) {
    _init();
  }

  final GetOrdersUseCase _getOrdersUseCase;
  final GetAcceptedApplicationsForDayUseCase _getAcceptedAppsUseCase;
  int _requestId = 0;

  void _init() {
    updateWeekOffset(0); // Initialize calendar days
  }

  Future<void> _loadOrdersForRange({
    required DateTime rangeStart,
    required DateTime rangeEnd,
    required DateTime dayForApps,
  }) async {
    final int requestId = ++_requestId;
    emit(state.copyWith(status: ViewOrdersStatus.loading));

    await handleError(
      emit: (ViewOrdersState s) {
        if (requestId == _requestId) emit(s);
      },
      action: () async {
        final List<OrderItem> orders = await _getOrdersUseCase(
          OrdersRangeArguments(start: rangeStart, end: rangeEnd),
        );
        final Map<String, List<Map<String, dynamic>>> apps =
            await _getAcceptedAppsUseCase(OrdersDayArguments(day: dayForApps));

        if (requestId != _requestId) {
          return;
        }

        final List<OrderItem> updatedOrders = _applyApplications(orders, apps);
        emit(
          state.copyWith(
            status: ViewOrdersStatus.success,
            orders: updatedOrders,
          ),
        );
        _updateDerivedState();
      },
      onError: (String message) => state.copyWith(
        status: ViewOrdersStatus.failure,
        errorMessage: message,
      ),
    );
  }

  void selectDate(DateTime date) {
    emit(state.copyWith(selectedDate: date));
    _refreshAcceptedApplications(date);
  }

  void selectFilterTab(String tabId) {
    emit(state.copyWith(filterTab: tabId));
    _updateDerivedState();
  }

  void updateWeekOffset(int offset) {
    final int newWeekOffset = state.weekOffset + offset;
    final List<DateTime> calendarDays = _calculateCalendarDays(newWeekOffset);
    final DateTime? selectedDate = state.selectedDate;
    final DateTime updatedSelectedDate =
        selectedDate != null &&
                calendarDays.any(
                  (DateTime day) => _isSameDay(day, selectedDate),
                )
            ? selectedDate
            : calendarDays.first;
    emit(
      state.copyWith(
        weekOffset: newWeekOffset,
        calendarDays: calendarDays,
        selectedDate: updatedSelectedDate,
      ),
    );

    _loadOrdersForRange(
      rangeStart: calendarDays.first,
      rangeEnd: calendarDays.last,
      dayForApps: updatedSelectedDate,
    );
  }

  void jumpToDate(DateTime date) {
    final DateTime target = DateTime(date.year, date.month, date.day);
    final DateTime startDate = _calculateCalendarDays(0).first;
    final int diffDays = target.difference(startDate).inDays;
    final int targetOffset = (diffDays / 7).floor();
    final List<DateTime> calendarDays = _calculateCalendarDays(targetOffset);

    emit(
      state.copyWith(
        weekOffset: targetOffset,
        calendarDays: calendarDays,
        selectedDate: target,
      ),
    );

    _loadOrdersForRange(
      rangeStart: calendarDays.first,
      rangeEnd: calendarDays.last,
      dayForApps: target,
    );
  }

  void _updateDerivedState() {
    final List<OrderItem> filteredOrders = _calculateFilteredOrders(state);
    final int activeCount = _calculateCategoryCount('active');
    final int completedCount = _calculateCategoryCount('completed');
    final int upNextCount = _calculateUpNextCount();

    emit(
      state.copyWith(
        filteredOrders: filteredOrders,
        activeCount: activeCount,
        completedCount: completedCount,
        upNextCount: upNextCount,
      ),
    );
  }

  Future<void> _refreshAcceptedApplications(DateTime day) async {
    await handleErrorWithResult(
      action: () async {
        final Map<String, List<Map<String, dynamic>>> apps =
            await _getAcceptedAppsUseCase(OrdersDayArguments(day: day));
        final List<OrderItem> updatedOrders = _applyApplications(
          state.orders,
          apps,
        );
        emit(state.copyWith(orders: updatedOrders));
        _updateDerivedState();
      },
      onError: (_) {
        // Keep existing data on failure, just log error via handleErrorWithResult
      },
    );
  }

  List<OrderItem> _applyApplications(
    List<OrderItem> orders,
    Map<String, List<Map<String, dynamic>>> apps,
  ) {
    return orders.map((OrderItem order) {
      final List<Map<String, dynamic>> confirmed =
          apps[order.id] ?? const <Map<String, dynamic>>[];
      if (confirmed.isEmpty) {
        return order;
      }

      final int filled = confirmed.length;
      final String status =
          filled >= order.workersNeeded ? 'FILLED' : order.status;
      return OrderItem(
        id: order.id,
        orderId: order.orderId,
        title: order.title,
        clientName: order.clientName,
        status: status,
        date: order.date,
        startTime: order.startTime,
        endTime: order.endTime,
        location: order.location,
        locationAddress: order.locationAddress,
        filled: filled,
        workersNeeded: order.workersNeeded,
        hourlyRate: order.hourlyRate,
        hours: order.hours,
        totalValue: order.totalValue,
        confirmedApps: confirmed,
      );
    }).toList();
  }

  bool _isSameDay(DateTime a, DateTime b) {
    return a.year == b.year && a.month == b.month && a.day == b.day;
  }

  List<DateTime> _calculateCalendarDays(int weekOffset) {
    final DateTime now = DateTime.now();
    final int jsDay = now.weekday == 7 ? 0 : now.weekday;
    final int daysSinceFriday = (jsDay + 2) % 7;

    final DateTime startDate = DateTime(now.year, now.month, now.day)
        .subtract(Duration(days: daysSinceFriday))
        .add(Duration(days: weekOffset * 7));

    return List<DateTime>.generate(
      7,
      (int index) => startDate.add(Duration(days: index)),
    );
  }

  List<OrderItem> _calculateFilteredOrders(ViewOrdersState state) {
    if (state.selectedDate == null) return <OrderItem>[];

    final String selectedDateStr = DateFormat(
      'yyyy-MM-dd',
    ).format(state.selectedDate!);

    // Filter by date
    final List<OrderItem> ordersOnDate =
        state.orders
            .where((OrderItem s) => s.date == selectedDateStr)
            .toList();

    // Sort by start time
    ordersOnDate.sort(
      (OrderItem a, OrderItem b) => a.startTime.compareTo(b.startTime),
    );

    if (state.filterTab == 'all') {
      final List<OrderItem> filtered =
          ordersOnDate
              .where(
                (OrderItem s) =>
                    // TODO(orders): move PENDING to its own tab once available.
                    <String>[
                      'OPEN',
                      'FILLED',
                      'CONFIRMED',
                      'PENDING',
                      'ASSIGNED',
                    ].contains(s.status),
              )
              .toList();
      print(
        'ViewOrders tab=all statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}',
      );
      return filtered;
    } else if (state.filterTab == 'active') {
      final List<OrderItem> filtered =
          ordersOnDate
              .where((OrderItem s) => s.status == 'IN_PROGRESS')
              .toList();
      print(
        'ViewOrders tab=active statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}',
      );
      return filtered;
    } else if (state.filterTab == 'completed') {
      final List<OrderItem> filtered =
          ordersOnDate
              .where((OrderItem s) => s.status == 'COMPLETED')
              .toList();
      print(
        'ViewOrders tab=completed statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}',
      );
      return filtered;
    }
    return <OrderItem>[];
  }

  int _calculateCategoryCount(String category) {
    if (state.selectedDate == null) return 0;

    final String selectedDateStr = DateFormat(
      'yyyy-MM-dd',
    ).format(state.selectedDate!);

    if (category == 'active') {
      return state.orders
          .where(
            (OrderItem s) =>
                s.date == selectedDateStr && s.status == 'IN_PROGRESS',
          )
          .length;
    } else if (category == 'completed') {
      return state.orders
          .where(
            (OrderItem s) =>
                s.date == selectedDateStr && s.status == 'COMPLETED',
          )
          .length;
    }
    return 0;
  }

  int _calculateUpNextCount() {
    if (state.selectedDate == null) return 0;

    final String selectedDateStr = DateFormat(
      'yyyy-MM-dd',
    ).format(state.selectedDate!);

    return state.orders
        .where(
          (OrderItem s) =>
              s.date == selectedDateStr &&
              <String>[
                'OPEN',
                'FILLED',
                'CONFIRMED',
                'PENDING',
                'ASSIGNED',
              ].contains(s.status),
        )
        .length;
  }
}

