import 'package:flutter/foundation.dart';

import 'package:pshared/models/payment/payment.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/payment/quotation/quotation.dart';
import 'package:pshared/provider/resource.dart';
import 'package:pshared/service/payment/service.dart';
import 'package:pshared/utils/exception.dart';

class PaymentProvider extends ChangeNotifier {
  late OrganizationsProvider _organization;
  late QuotationProvider _quotation;

  Resource<Payment> _payment = Resource(
    data: null,
    isLoading: false,
    error: null,
  );
  bool _isLoaded = false;

  void update(OrganizationsProvider organization, QuotationProvider quotation) {
    _quotation = quotation;
    _organization = organization;
  }

  Payment? get payment => _payment.data;
  bool get isLoading => _payment.isLoading;
  Exception? get error => _payment.error;
  bool get isReady =>
      _isLoaded && !_payment.isLoading && _payment.error == null;

  void _setResource(Resource<Payment> payment) {
    _payment = payment;
    notifyListeners();
  }

  Future<Payment?> pay({
    String? idempotencyKey,
    String? clientPaymentRef,
    Map<String, String>? metadata,
  }) async {
    if (!_organization.isOrganizationSet) throw StateError('Organization is not set');
    final quoteRef = _quotation.quotation?.quoteRef;
    if (quoteRef == null || quoteRef.isEmpty) {
      throw StateError('Quotation reference is not set');
    }
    final expiresAt = _quotation.quoteExpiresAt;
    if (expiresAt != null && expiresAt.isBefore(DateTime.now().toUtc())) {
      throw StateError('Quotation is expired');
    }

    _setResource(_payment.copyWith(isLoading: true, error: null));
    try {
      final resolvedIdempotencyKey =
          idempotencyKey ?? _quotation.quotation?.idempotencyKey;
      final response = await PaymentService.pay(
        _organization.current.id,
        quoteRef,
        idempotencyKey: resolvedIdempotencyKey,
        clientPaymentRef: clientPaymentRef,
        metadata: metadata,
      );
      _isLoaded = true;
      _setResource(
        _payment.copyWith(data: response, isLoading: false, error: null),
      );
    } catch (e) {
      _setResource(
        _payment.copyWith(data: null, error: toException(e), isLoading: false),
      );
    }
    return _payment.data;
  }

  void reset() {
    _setResource(Resource(data: null, isLoading: false, error: null));
    _isLoaded = false;
  }
}
