import 'package:flutter/foundation.dart';

import 'package:uuid/uuid.dart';

import 'package:pshared/api/requests/payment/quotes.dart';
import 'package:pshared/data/mapper/payment/intent/payment.dart';
import 'package:pshared/models/payment/intent.dart';
import 'package:pshared/models/payment/kind.dart';
import 'package:pshared/models/payment/quote/quotes.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/payment/auto_refresh.dart';
import 'package:pshared/provider/resource.dart';
import 'package:pshared/service/payment/multiple.dart';
import 'package:pshared/utils/exception.dart';


class MultiQuotationProvider extends ChangeNotifier {
  static const Duration _autoRefreshLead = Duration(seconds: 5);

  OrganizationsProvider? _organizations;
  String? _loadedOrganizationRef;

  Resource<PaymentQuotes> _quotation = Resource(data: null);
  final AutoRefreshScheduler _autoRefresh = AutoRefreshScheduler();

  List<PaymentIntent>? _lastIntents;
  bool _lastPreviewOnly = false;
  Map<String, String>? _lastMetadata;

  Resource<PaymentQuotes> get resource => _quotation;
  PaymentQuotes? get quotation => _quotation.data;
  bool get isLoading => _quotation.isLoading;
  Exception? get error => _quotation.error;
  bool get canRefresh => _lastIntents != null && _lastIntents!.isNotEmpty;
  bool get isReady =>
      quotation != null && !_quotation.isLoading && _quotation.error == null;

  DateTime? get quoteExpiresAt {
    final items = quotation?.items;
    if (items == null || items.isEmpty) return null;

    int? minExpiresAt;
    for (final quote in items) {
      final expiresAtUnixMs = quote.fxQuote?.expiresAtUnixMs;
      if (expiresAtUnixMs == null) continue;
      minExpiresAt = minExpiresAt == null
          ? expiresAtUnixMs
          : (expiresAtUnixMs < minExpiresAt ? expiresAtUnixMs : minExpiresAt);
    }

    if (minExpiresAt == null) return null;
    return DateTime.fromMillisecondsSinceEpoch(minExpiresAt, isUtc: true);
  }

  void update(OrganizationsProvider organizations) {
    _organizations = organizations;
    if (!organizations.isOrganizationSet) {
      reset();
      return;
    }

    final orgRef = organizations.current.id;
    if (_loadedOrganizationRef != orgRef) {
      _loadedOrganizationRef = orgRef;
      reset();
    }
  }

  Future<PaymentQuotes?> quotePayments(
    List<PaymentIntent> intents, {
    bool previewOnly = false,
    String? idempotencyKey,
    Map<String, String>? metadata,
  }) async {
    final organization = _organizations;
    if (organization == null || !organization.isOrganizationSet) {
      throw StateError('Organization is not set');
    }
    if (intents.isEmpty) {
      throw StateError('At least one payment intent is required');
    }
    if (intents.any((intent) => !_isQuotable(intent))) {
      throw StateError(
        'Each payment intent must include kind, source, destination, and amount',
      );
    }

    _lastIntents = List<PaymentIntent>.from(intents);
    _lastPreviewOnly = previewOnly;
    _lastMetadata = metadata == null
        ? null
        : Map<String, String>.from(metadata);

    _setResource(_quotation.copyWith(isLoading: true, error: null));
    try {
      final effectiveIdempotencyKey = previewOnly
          ? ''
          : (idempotencyKey ?? const Uuid().v4());
      final response = await MultiplePaymentsService.getQuotation(
        organization.current.id,
        QuotePaymentsRequest(
          idempotencyKey: effectiveIdempotencyKey,
          metadata: metadata,
          intents: intents.map((intent) => intent.toDTO()).toList(),
          previewOnly: previewOnly,
        ),
      );

      _setResource(
        _quotation.copyWith(data: response, isLoading: false, error: null),
      );
    } catch (e) {
      _setResource(
        _quotation.copyWith(
          data: null,
          isLoading: false,
          error: toException(e),
        ),
      );
    }

    return _quotation.data;
  }

  Future<PaymentQuotes?> refreshQuotation() async {
    final intents = _lastIntents;
    if (intents == null || intents.isEmpty) return null;

    return quotePayments(
      intents,
      previewOnly: _lastPreviewOnly,
      metadata: _lastMetadata,
    );
  }

  void reset() {
    _lastIntents = null;
    _lastPreviewOnly = false;
    _lastMetadata = null;
    _quotation = Resource(data: null);
    _syncAutoRefresh();
    notifyListeners();
  }

  bool _isQuotable(PaymentIntent intent) {
    if (intent.kind == PaymentKind.unspecified) return false;
    if (intent.source == null) return false;
    if (intent.destination == null) return false;
    return intent.amount != null;
  }

  void _setResource(Resource<PaymentQuotes> quotation) {
    _quotation = quotation;
    _syncAutoRefresh();
    notifyListeners();
  }

  void _syncAutoRefresh() {
    final scheduledAt = quoteExpiresAt?.subtract(_autoRefreshLead);
    _autoRefresh.setEnabled(true);
    _autoRefresh.sync(
      isLoading: isLoading,
      canRefresh: canRefresh,
      scheduledAt: scheduledAt,
      onRefresh: () async {
        await refreshQuotation();
      },
    );
  }

  @override
  void dispose() {
    _autoRefresh.dispose();
    super.dispose();
  }
}
