import 'package:flutter/material.dart';

import 'package:pshared/provider/payout_verification.dart';

import 'package:pweb/controllers/common/cooldown.dart';
import 'package:pweb/models/state/flow_status.dart';


class PayoutVerificationController extends ChangeNotifier {
  PayoutVerificationController() {
    _cooldown = CooldownController(onTick: () => notifyListeners());
  }

  PayoutVerificationProvider? _provider;

  FlowStatus _status = FlowStatus.idle;
  Object? _error;
  late final CooldownController _cooldown;
  String? _contextKey;
  String? _cooldownContextKey;

  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 => _cooldown.remainingSeconds;
  bool get isCooldownActive => _cooldown.isActive;
  bool isCooldownActiveFor(String? contextKey) {
    if (!_cooldown.isActive) return false;
    return _cooldownContextKey == contextKey;
  }

  int cooldownRemainingSecondsFor(String? contextKey) {
    if (_cooldownContextKey != contextKey) return 0;
    return _cooldown.remainingSeconds;
  }

  void update(PayoutVerificationProvider provider) {
    if (identical(_provider, provider)) return;
    _provider?.removeListener(_onProviderChanged);
    _provider = provider;
    _provider?.addListener(_onProviderChanged);
    _syncCooldown(provider.cooldownUntil);
  }

  void setContextKey(String? contextKey) {
    if (_contextKey == contextKey) return;
    _contextKey = contextKey;
    _cooldownContextKey = null;
    _cooldown.stop();
  }

  Future<void> requestCode() async {
    final provider = _provider;
    if (provider == null) {
      throw StateError('Payout verification provider is not ready');
    }
    _bindCooldownContext();
    _error = null;
    _setStatus(FlowStatus.submitting);
    try {
      await provider.requestCode();
      _setStatus(FlowStatus.idle);
    } catch (e) {
      _error = e;
      _setStatus(FlowStatus.error);
      rethrow;
    }
  }

  Future<void> 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<void> resendCode() async {
    final provider = _provider;
    if (provider == null) {
      throw StateError('Payout verification provider is not ready');
    }
    if (isResending || isCooldownActive) return;
    _bindCooldownContext();
    _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);
    _cooldown.stop();
    _cooldownContextKey = null;
    _contextKey = null;
    _provider?.reset();
  }

  void resetStatus() {
    _error = null;
    _setStatus(FlowStatus.idle);
  }

  void _onProviderChanged() {
    _syncCooldown(_provider?.cooldownUntil);
    notifyListeners();
  }

  void _syncCooldown(DateTime? until) {
    if (_cooldownContextKey == null || _cooldownContextKey != _contextKey) {
      _cooldown.stop(notify: _cooldown.isActive);
      return;
    }
    if (until == null) {
      _cooldown.stop(notify: _cooldown.isActive);
      return;
    }

    if (!_isCooldownActive(until)) {
      _cooldown.stop(notify: _cooldown.isActive);
      return;
    }

    final currentUntil = _cooldown.until;
    if (currentUntil == null || !currentUntil.isAtSameMomentAs(until)) {
      _cooldown.syncUntil(until, notify: true);
    }
  }

  bool _isCooldownActive(DateTime until) => until.isAfter(DateTime.now());

  void _bindCooldownContext() {
    final key = _contextKey;
    if (key == null) {
      _cooldownContextKey = null;
      _cooldown.stop();
      return;
    }
    if (_cooldownContextKey == key) return;
    _cooldown.stop();
    _cooldownContextKey = key;
  }

  void _setStatus(FlowStatus status) {
    if (_status == status) return;
    _status = status;
    notifyListeners();
  }

  @override
  void dispose() {
    _provider?.removeListener(_onProviderChanged);
    _cooldown.dispose();
    super.dispose();
  }
}
