import 'package:flutter/material.dart';

import 'package:collection/collection.dart';

import 'package:pshared/models/payment/operation.dart';
import 'package:pshared/models/payment/payment.dart';
import 'package:pshared/models/payment/source_type.dart';
import 'package:pshared/models/payment/status.dart';
import 'package:pshared/provider/payment/payments.dart';

import 'package:pweb/models/state/load_more_state.dart';
import 'package:pweb/utils/report/operations/operations.dart';
import 'package:pweb/utils/report/payment_mapper.dart';
import 'package:pweb/utils/report/source_filter.dart';


class ReportOperationsController extends ChangeNotifier {
  PaymentsProvider? _payments;
  PaymentSourceType? _sourceType;
  Set<String> _sourceRefs = const <String>{};
  DateTimeRange? _selectedRange;
  final Set<OperationStatus> _selectedStatuses = {};
  List<Payment> _paymentItems = const [];
  List<OperationItem> _operations = const [];
  List<OperationItem> _filtered = const [];

  List<OperationItem> get operations => _operations;
  List<OperationItem> get filteredOperations => _filtered;
  DateTimeRange? get selectedRange => _selectedRange;
  Set<OperationStatus> get selectedStatuses =>
      UnmodifiableSetView(_selectedStatuses);

  bool get isLoading => _payments?.isLoading ?? false;
  Exception? get error => _payments?.error;
  LoadMoreState get loadMoreState {
    if (_payments?.isLoadingMore ?? false) {
      return LoadMoreState.loading;
    }
    if (_payments?.canLoadMore ?? false) {
      return LoadMoreState.available;
    }
    return LoadMoreState.hidden;
  }

  void update(
    PaymentsProvider provider, {
    PaymentSourceType? sourceType,
    String? sourceRef,
    List<String>? sourceRefs,
  }) {
    if (!identical(_payments, provider)) {
      _payments = provider;
    }
    _sourceType = sourceType;
    final effectiveSourceRefs =
        sourceRefs ??
        (sourceRef == null ? const <String>[] : <String>[sourceRef]);
    _sourceRefs = _normalizeRefs(effectiveSourceRefs);
    _rebuildOperations();
  }

  void setRange(DateTimeRange? range) {
    if (_isSameRange(_selectedRange, range)) return;
    _selectedRange = range;
    _rebuildFiltered();
  }

  void toggleStatus(OperationStatus status) {
    if (_selectedStatuses.contains(status)) {
      _selectedStatuses.remove(status);
    } else {
      _selectedStatuses.add(status);
    }
    _rebuildFiltered();
  }

  void clearFilters() {
    if (_selectedRange == null && _selectedStatuses.isEmpty) return;
    _selectedRange = null;
    _selectedStatuses.clear();
    _rebuildFiltered();
  }

  Future<void> refresh() async {
    await _payments?.refresh();
  }

  Future<void> loadMore() async {
    await _payments?.loadMore();
  }

  void _rebuildOperations() {
    _paymentItems = _payments?.payments ?? const [];
    _operations = _paymentItems
        .where(_matchesCurrentSource)
        .map(mapPaymentToOperation)
        .toList();
    _rebuildFiltered(notify: true);
  }

  void _rebuildFiltered({bool notify = true}) {
    _filtered = _applyFilters(sortOperations(_operations));
    if (notify) {
      notifyListeners();
    }
  }

  List<OperationItem> _applyFilters(List<OperationItem> operations) {
    if (_selectedRange == null && _selectedStatuses.isEmpty) {
      return operations;
    }

    final filtered = operations.where((op) {
      final statusMatch =
          _selectedStatuses.isEmpty || _selectedStatuses.contains(op.status);
      final dateMatch =
          _selectedRange == null ||
          isUnknownDate(op.date) ||
          (op.date.isAfter(
                _selectedRange!.start.subtract(const Duration(seconds: 1)),
              ) &&
              op.date.isBefore(
                _selectedRange!.end.add(const Duration(seconds: 1)),
              ));
      return statusMatch && dateMatch;
    }).toList();

    return filtered;
  }

  bool _matchesCurrentSource(Payment payment) {
    final sourceType = _sourceType;
    if (sourceType == null || _sourceRefs.isEmpty) return true;
    return _sourceRefs.firstWhereOrNull(
          (sourceRef) => paymentMatchesSource(
            payment,
            sourceType: sourceType,
            sourceRef: sourceRef,
          ),
        ) !=
        null;
  }

  Set<String> _normalizeRefs(List<String> refs) {
    final normalized = refs
        .map((value) => value.trim())
        .where((value) => value.isNotEmpty)
        .toSet();
    return normalized;
  }

  bool _isSameRange(DateTimeRange? left, DateTimeRange? right) {
    if (left == null && right == null) return true;
    if (left == null || right == null) return false;
    return left.start.isAtSameMomentAs(right.start) &&
        left.end.isAtSameMomentAs(right.end);
  }
}
