import 'package:votianlt_app/services/developer.dart' as developer;

/// A lightweight in-app message bus ("dart_mq") for pub/sub style communication.
///
// Usage:
//   final mq = DartMQ();
//   final sub = mq.subscribe<bool>('connection/status', (isOnline) { /* ... */ });
//   mq.publish('connection/status', true);
//   sub.cancel();
class DartMQ {
  DartMQ._internal();
  static final DartMQ _instance = DartMQ._internal();
  factory DartMQ() => _instance;

  final Map<String, List<_DartMQSubscriber>> _subscribers = {};

  /// Subscribe to a topic. Returns a cancellable subscription handle.
  DartMQSubscription subscribe<T>(String topic, void Function(T data) handler) {
    final sub = _DartMQSubscriber<T>(topic: topic, handler: handler);
    final list = _subscribers.putIfAbsent(topic, () => <_DartMQSubscriber>[]);
    list.add(sub);
    return DartMQSubscription._(this, sub);
  }

  /// Publish a message to a topic. If no subscribers exist, this is a no-op.
  void publish<T>(String topic, T data) {
    final list = _subscribers[topic];
    if (list == null || list.isEmpty) return;

    // Make a defensive copy to allow cancellation during iteration.
    final current = List<_DartMQSubscriber>.from(list);
    for (final s in current) {
      // Only deliver if types match; otherwise, try dynamic fallback
      if (s is _DartMQSubscriber<T>) {
        try {
          s.handler(data);
        } catch (e, stackTrace) {
          developer.log(
            'Error delivering message to subscriber on topic "$topic": $e',
          );
          developer.log('Stack trace: $stackTrace');
        }
      } else {
        // Fallback delivery for handlers expecting dynamic or different T
        try {
          final dynamicHandler = s.handler as dynamic;
          dynamicHandler(data);
        } catch (e, stackTrace) {
          developer.log(
            'Error delivering dynamic message to subscriber on topic "$topic": $e',
          );
          developer.log('Stack trace: $stackTrace');
        }
      }
    }
  }

  void _cancel(_DartMQSubscriber subscriber) {
    final list = _subscribers[subscriber.topic];
    if (list == null) return;
    list.remove(subscriber);
    if (list.isEmpty) {
      _subscribers.remove(subscriber.topic);
    }
  }
}

/// Cancellable subscription handle
class DartMQSubscription {
  final DartMQ _mq;
  final _DartMQSubscriber _subscriber;
  bool _isCancelled = false;

  DartMQSubscription._(this._mq, this._subscriber);

  void cancel() {
    if (_isCancelled) return;
    _isCancelled = true;
    _mq._cancel(_subscriber);
  }
}

class _DartMQSubscriber<T> {
  final String topic;
  final void Function(T data) handler;
  _DartMQSubscriber({required this.topic, required this.handler});
}

/// Common topics used in the app
class MQTopics {
  static const connectionStatus = 'connection/status'; // bool
  static const authResponse = 'auth/response'; // Map<String, dynamic>
  static const jobsResponse = 'jobs/response'; // List<dynamic>
  static const taskEvents = 'task/events'; // Map<String, dynamic>
  static const jobsUpdated = 'app/jobsUpdated'; // void/null
  static const jobDeleted = 'job/deleted'; // Map<String, dynamic> {jobId, jobNumber, deletedAt}
  static const jobCreated = 'job/created'; // Map<String, dynamic> - full job data
  static const chatIncoming = 'chat/incoming'; // ChatMessage
  static const chatOutgoing = 'chat/outgoing'; // ChatMessage
}
