import 'dart:async'; import 'package:flutter/material.dart'; import 'package:pshared/provider/payout_verification.dart'; import 'package:pweb/models/flow_status.dart'; class PayoutVerificationController extends ChangeNotifier { PayoutVerificationProvider? _provider; FlowStatus _status = FlowStatus.idle; Object? _error; Timer? _cooldownTimer; int _cooldownRemainingSeconds = 0; DateTime? _cooldownUntil; FlowStatus get status => _status; bool get isSubmitting => _status == FlowStatus.submitting; bool get isResending => _status == FlowStatus.resending; bool get hasError => _status == FlowStatus.error; bool get verificationSuccess => _status == FlowStatus.success; Object? get error => _error; String get target => _provider?.target ?? ''; int get cooldownRemainingSeconds => _cooldownRemainingSeconds; bool get isCooldownActive => _cooldownRemainingSeconds > 0; void update(PayoutVerificationProvider provider) { if (identical(_provider, provider)) return; _provider?.removeListener(_onProviderChanged); _provider = provider; _provider?.addListener(_onProviderChanged); _syncCooldown(provider.cooldownUntil); } Future requestCode() async { final provider = _provider; if (provider == null) { throw StateError('Payout verification provider is not ready'); } _error = null; _setStatus(FlowStatus.submitting); try { await provider.requestCode(); _setStatus(FlowStatus.idle); } catch (e) { _error = e; _setStatus(FlowStatus.error); rethrow; } } Future submitCode(String code) async { final provider = _provider; if (provider == null) { throw StateError('Payout verification provider is not ready'); } _error = null; _setStatus(FlowStatus.submitting); try { await provider.confirmCode(code); _setStatus(FlowStatus.success); } catch (e) { _error = e; _setStatus(FlowStatus.error); } } Future resendCode() async { final provider = _provider; if (provider == null) { throw StateError('Payout verification provider is not ready'); } if (isResending || isCooldownActive) return; _error = null; _setStatus(FlowStatus.resending); try { await provider.resendCode(); _setStatus(FlowStatus.idle); } catch (e) { _error = e; _setStatus(FlowStatus.error); } } void reset() { _error = null; _setStatus(FlowStatus.idle); _stopCooldown(); _provider?.reset(); } void resetStatus() { _error = null; _setStatus(FlowStatus.idle); } void _onProviderChanged() { _syncCooldown(_provider?.cooldownUntil); notifyListeners(); } void _syncCooldown(DateTime? until) { if (until == null) { _stopCooldown(notify: _cooldownRemainingSeconds != 0); return; } if (!_isCooldownActive(until) && _cooldownRemainingSeconds != 0) { _stopCooldown(notify: true); return; } if (_cooldownUntil == null || _cooldownUntil != until) { _startCooldownUntil(until); } } void _startCooldownUntil(DateTime until) { _cooldownTimer?.cancel(); _cooldownUntil = until; _cooldownRemainingSeconds = _cooldownRemaining(); if (_cooldownRemainingSeconds <= 0) { _cooldownTimer = null; _cooldownUntil = null; notifyListeners(); return; } _cooldownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { final remaining = _cooldownRemaining(); if (remaining <= 0) { _stopCooldown(notify: true); return; } if (remaining != _cooldownRemainingSeconds) { _cooldownRemainingSeconds = remaining; notifyListeners(); } }); notifyListeners(); } bool _isCooldownActive(DateTime until) => until.isAfter(DateTime.now()); int _cooldownRemaining() { final until = _cooldownUntil; if (until == null) return 0; final remaining = until.difference(DateTime.now()).inSeconds; return remaining < 0 ? 0 : remaining; } void _stopCooldown({bool notify = false}) { _cooldownTimer?.cancel(); _cooldownTimer = null; final hadCooldown = _cooldownRemainingSeconds != 0; _cooldownRemainingSeconds = 0; _cooldownUntil = null; if (notify && hadCooldown) { notifyListeners(); } } void _setStatus(FlowStatus status) { if (_status == status) return; _status = status; notifyListeners(); } @override void dispose() { _provider?.removeListener(_onProviderChanged); _stopCooldown(); super.dispose(); } }