import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
import 'package:nearle/views/helpers/constants/mqtt_constants.dart';
import 'package:shared_preferences/shared_preferences.dart';

class NearleMqttService {
  static final NearleMqttService _instance = NearleMqttService._internal();

  factory NearleMqttService() {
    return _instance;
  }

  NearleMqttService._internal();

  MqttServerClient? _client;
  bool _isConnected = false;
  bool _isConnecting = false; // Prevents concurrent connection attempts
  String? _currentRiderId;

  // Unique per isolate startup — prevents client ID collision between
  // main isolate and background foreground-service isolate.
  static final String _sessionSuffix =
      DateTime.now().millisecondsSinceEpoch.toRadixString(36);

  bool get isConnected => _isConnected;

  Future<void> connect() async {
    if (_isConnected || _isConnecting) return;
    _isConnecting = true;

    try {
      final prefs = await SharedPreferences.getInstance();
      final riderIdLong = prefs.getInt('userId') ?? prefs.getInt('userid');
      if (riderIdLong == null || riderIdLong <= 0) {
        debugPrint('[MQTT] Cannot connect: No rider ID found in preferences.');
        return;
      }
      _currentRiderId = riderIdLong.toString();

      const host = MqttConstants.brokerHost;
      final clientId = 'rider_${_currentRiderId}_$_sessionSuffix';

      _client = MqttServerClient(host, clientId);
      _client!.port = MqttConstants.brokerPort;
      _client!.keepAlivePeriod = 30;
      _client!.autoReconnect = true;
      _client!.logging(on: false);

      final lwtTopic =
          MqttConstants.topicRiderStatus.replaceAll('{riderId}', _currentRiderId!);
      _client!.onDisconnected = _onDisconnected;
      _client!.onConnected = _onConnected;
      _client!.onAutoReconnect = _onAutoReconnect;
      _client!.onSubscribed = _onSubscribed;

      final connMessage = MqttConnectMessage()
          .withClientIdentifier(clientId)
          .authenticateAs(MqttConstants.username, MqttConstants.passwordString)
          .withWillTopic(lwtTopic)
          .withWillMessage(MqttConstants.statusOffline)
          .withWillQos(MqttQos.atLeastOnce)
          .withWillRetain()
          .startClean();

      _client!.connectionMessage = connMessage;

      debugPrint('[MQTT] Connecting to $host as $clientId...');
      await _client!.connect();
    } catch (e) {
      debugPrint('[MQTT] Connection failed: $e');
      _cleanDisconnect();
    } finally {
      _isConnecting = false;
    }
  }

  /// Cleanly disconnects and resets all state. Call on logout or duty end.
  void disconnect() {
    _publishStatus(MqttConstants.statusOffline);
    _cleanDisconnect();
    debugPrint('[MQTT] Disconnected and state cleared.');
  }

  void _cleanDisconnect() {
    _client?.disconnect();
    _client = null;
    _isConnected = false;
    _currentRiderId = null;
  }

  void _onConnected() {
    _isConnected = true;
    debugPrint('[MQTT] Connected successfully.');
    _publishStatus(MqttConstants.statusOnline);
  }

  void _onDisconnected() {
    _isConnected = false;
    debugPrint('[MQTT] Disconnected from broker.');
  }

  void _onAutoReconnect() {
    debugPrint('[MQTT] Auto-reconnecting...');
  }

  void _onSubscribed(String topic) {
    debugPrint('[MQTT] Subscribed to topic: $topic');
  }

  void _publishStatus(String status) {
    if (!_isConnected || _currentRiderId == null) return;

    final topic =
        MqttConstants.topicRiderStatus.replaceAll('{riderId}', _currentRiderId!);
    final builder = MqttClientPayloadBuilder();
    builder.addString(status);

    _client!.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!,
        retain: true);
    debugPrint('[MQTT] Published status: $status to $topic');
  }

  // --- PUBLIC API ---

  void updateStatus(String status) {
    _publishStatus(status);
  }

  void publishProfile(Map<String, dynamic> profileData) {
    if (!_isConnected || _currentRiderId == null) return;

    final topic =
        MqttConstants.topicRiderProfile.replaceAll('{riderId}', _currentRiderId!);
    final builder = MqttClientPayloadBuilder();
    builder.addString(jsonEncode(profileData));

    _client!.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!,
        retain: true);
    debugPrint('[MQTT] Published profile to $topic');
  }

  void publishLocation(Map<String, dynamic> locationData) {
    if (!_isConnected || _currentRiderId == null) return;

    final topic =
        MqttConstants.topicRiderLocation.replaceAll('{riderId}', _currentRiderId!);
    final builder = MqttClientPayloadBuilder();
    builder.addString(jsonEncode(locationData));

    _client!.publishMessage(topic, MqttQos.atMostOnce, builder.payload!);
  }

  void publishTelemetry(Map<String, dynamic> telemetryData) {
    if (!_isConnected || _currentRiderId == null) return;

    final topic =
        MqttConstants.topicRiderTelemetry.replaceAll('{riderId}', _currentRiderId!);
    final builder = MqttClientPayloadBuilder();
    builder.addString(jsonEncode(telemetryData));

    _client!.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!);
    debugPrint('[MQTT] Published telemetry data.');
  }

  void publishLog(String eventName, Map<String, dynamic> data) {
    if (!_isConnected || _currentRiderId == null) return;

    final topic =
        '${MqttConstants.topicRiderLogs.replaceAll('{riderId}', _currentRiderId!)}/$eventName';
    final builder = MqttClientPayloadBuilder();
    builder.addString(jsonEncode(data));

    _client!.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!);
    debugPrint('[MQTT] Published log: $eventName');
  }

  void publish(String subTopic, dynamic data) {
    if (!_isConnected || _currentRiderId == null) return;

    final topic = 'nearle/riders/$_currentRiderId/$subTopic';
    final builder = MqttClientPayloadBuilder();
    if (data is String) {
      builder.addString(data);
    } else {
      builder.addString(jsonEncode(data));
    }

    _client!.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!);
    debugPrint('[MQTT] Published to $topic');
  }
}
